@cuylabs/channel-teams-agent-core 0.4.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/LICENSE +201 -0
- package/README.md +80 -0
- package/dist/chunk-ZULHZARA.js +32 -0
- package/dist/extensions.d.ts +1 -0
- package/dist/extensions.js +2 -0
- package/dist/index.d.ts +250 -0
- package/dist/index.js +836 -0
- package/dist/proactive.d.ts +40 -0
- package/dist/proactive.js +20 -0
- package/docs/README.md +16 -0
- package/package.json +93 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Conversation,
|
|
3
|
+
ConversationBuilder,
|
|
4
|
+
ConversationReferenceBuilder,
|
|
5
|
+
CreateConversationOptionsBuilder,
|
|
6
|
+
captureM365ConversationReference,
|
|
7
|
+
captureTeamsConversationReference,
|
|
8
|
+
continueM365Conversation,
|
|
9
|
+
restoreM365ConversationReference
|
|
10
|
+
} from "./chunk-ZULHZARA.js";
|
|
11
|
+
|
|
12
|
+
// src/index.ts
|
|
13
|
+
export * from "@cuylabs/channel-teams";
|
|
14
|
+
|
|
15
|
+
// src/adapter.ts
|
|
16
|
+
import {
|
|
17
|
+
createM365ChannelAdapter,
|
|
18
|
+
extractUserIdentity,
|
|
19
|
+
runWithM365TurnContext
|
|
20
|
+
} from "@cuylabs/channel-m365-agent-core";
|
|
21
|
+
import { ActivityTypes } from "@microsoft/agents-activity";
|
|
22
|
+
import { parseTeamsActivity, sendTeamsInvoke } from "@cuylabs/channel-teams";
|
|
23
|
+
function createTeamsChannelAdapter(options) {
|
|
24
|
+
const {
|
|
25
|
+
prepareTurn,
|
|
26
|
+
handlers,
|
|
27
|
+
onInvoke: fallbackInvoke,
|
|
28
|
+
onTurn: onTurnHook,
|
|
29
|
+
strictChannelData,
|
|
30
|
+
...baseOptions
|
|
31
|
+
} = options;
|
|
32
|
+
const base = createM365ChannelAdapter({
|
|
33
|
+
...baseOptions,
|
|
34
|
+
prepareTurn: async (request) => {
|
|
35
|
+
const teams = parseTeamsActivity(request.turnContext, {
|
|
36
|
+
strictChannelData
|
|
37
|
+
});
|
|
38
|
+
const prepared = prepareTurn ? await prepareTurn({
|
|
39
|
+
...request,
|
|
40
|
+
teams,
|
|
41
|
+
channelData: teams.channelData
|
|
42
|
+
}) : void 0;
|
|
43
|
+
return {
|
|
44
|
+
...prepared ?? {},
|
|
45
|
+
scopeName: prepared?.scopeName?.trim() || "teams-activity",
|
|
46
|
+
scopeAttributes: {
|
|
47
|
+
teamsKind: teams.kind,
|
|
48
|
+
teamsSurface: teams.surface,
|
|
49
|
+
teamsTenantId: teams.tenantId,
|
|
50
|
+
teamsTeamId: teams.teamId,
|
|
51
|
+
teamsChannelId: teams.channelThreadId,
|
|
52
|
+
teamsMeetingId: teams.meetingId,
|
|
53
|
+
teamsInvokeName: teams.invokeName,
|
|
54
|
+
teamsEventType: teams.eventType,
|
|
55
|
+
...prepared?.scopeAttributes ?? {}
|
|
56
|
+
},
|
|
57
|
+
context: {
|
|
58
|
+
...prepared?.context ?? {},
|
|
59
|
+
teams
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
onInvoke: async (context) => {
|
|
64
|
+
const teams = parseTeamsActivity(context, {
|
|
65
|
+
strictChannelData
|
|
66
|
+
});
|
|
67
|
+
const dispatch = await dispatchTeamsHandler(
|
|
68
|
+
handlers,
|
|
69
|
+
teams.kind,
|
|
70
|
+
await createHandlerContext(options, base, context, teams)
|
|
71
|
+
);
|
|
72
|
+
if (!dispatch.handled && fallbackInvoke) {
|
|
73
|
+
await fallbackInvoke(context);
|
|
74
|
+
} else if (!dispatch.handled) {
|
|
75
|
+
await sendTeamsInvoke(context, void 0, 501);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
async function coreHandler(context) {
|
|
80
|
+
const teams = parseTeamsActivity(context, {
|
|
81
|
+
strictChannelData
|
|
82
|
+
});
|
|
83
|
+
if (teams.channelId !== "msteams") {
|
|
84
|
+
await base.handler(context);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const isTeamsSpecificEvent = context.activity.type === ActivityTypes.Event && teams.kind !== "other";
|
|
88
|
+
const isTeamsSpecificActivity = context.activity.type === ActivityTypes.ConversationUpdate || context.activity.type === ActivityTypes.MessageUpdate || context.activity.type === ActivityTypes.MessageDelete || context.activity.type === ActivityTypes.MessageReaction || isTeamsSpecificEvent;
|
|
89
|
+
if (isTeamsSpecificActivity) {
|
|
90
|
+
const dispatch = await dispatchTeamsHandler(
|
|
91
|
+
handlers,
|
|
92
|
+
teams.kind,
|
|
93
|
+
await createHandlerContext(options, base, context, teams)
|
|
94
|
+
);
|
|
95
|
+
if (dispatch.handled) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
await base.handler(context);
|
|
100
|
+
}
|
|
101
|
+
async function handler(context) {
|
|
102
|
+
if (onTurnHook) {
|
|
103
|
+
await onTurnHook(context, () => coreHandler(context));
|
|
104
|
+
} else {
|
|
105
|
+
await coreHandler(context);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
...base,
|
|
110
|
+
handler,
|
|
111
|
+
m365: base
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function createHandlerContext(options, base, turnContext, teams = parseTeamsActivity(turnContext)) {
|
|
115
|
+
const conversationId = turnContext.activity.conversation?.id ?? "unknown";
|
|
116
|
+
const channelId = turnContext.activity.channelId ?? "unknown";
|
|
117
|
+
const sessionId = await base.getSessionId(conversationId, channelId);
|
|
118
|
+
const user = extractUserIdentity(turnContext);
|
|
119
|
+
const source = resolveSource(options);
|
|
120
|
+
const ambient = {
|
|
121
|
+
turnContext,
|
|
122
|
+
user,
|
|
123
|
+
sessionId,
|
|
124
|
+
message: (turnContext.activity.text ?? "").trim(),
|
|
125
|
+
teams,
|
|
126
|
+
context: { teams }
|
|
127
|
+
};
|
|
128
|
+
const context = {
|
|
129
|
+
turnContext,
|
|
130
|
+
user,
|
|
131
|
+
teams,
|
|
132
|
+
channelData: teams.channelData,
|
|
133
|
+
sessionId,
|
|
134
|
+
source,
|
|
135
|
+
runAgent(message, runOptions) {
|
|
136
|
+
return runAgentWithTeamsContext(
|
|
137
|
+
ambient,
|
|
138
|
+
source,
|
|
139
|
+
sessionId,
|
|
140
|
+
message,
|
|
141
|
+
runOptions
|
|
142
|
+
);
|
|
143
|
+
},
|
|
144
|
+
async sendInvoke(body, status = 200) {
|
|
145
|
+
setInvokeResponseSent(context, true);
|
|
146
|
+
await sendTeamsInvoke(turnContext, body, status);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
setInvokeResponseSent(context, false);
|
|
150
|
+
return context;
|
|
151
|
+
}
|
|
152
|
+
async function dispatchTeamsHandler(handlers, kind, context) {
|
|
153
|
+
const handler = resolveHandler(handlers, kind);
|
|
154
|
+
if (!handler) {
|
|
155
|
+
return { handled: false };
|
|
156
|
+
}
|
|
157
|
+
const result = await handler(context);
|
|
158
|
+
if (context.turnContext.activity.type === ActivityTypes.Invoke && result !== "continue") {
|
|
159
|
+
if (!getInvokeResponseSent(context) && isTeamsInvokeResult(result)) {
|
|
160
|
+
await context.sendInvoke(result.body, result.status);
|
|
161
|
+
} else if (!getInvokeResponseSent(context) && result === void 0) {
|
|
162
|
+
await context.sendInvoke();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { handled: result !== "continue" };
|
|
166
|
+
}
|
|
167
|
+
function resolveHandler(handlers, kind) {
|
|
168
|
+
switch (kind) {
|
|
169
|
+
case "conversation-update":
|
|
170
|
+
return handlers?.conversationUpdate;
|
|
171
|
+
case "members-added":
|
|
172
|
+
return handlers?.membersAdded ?? handlers?.conversationUpdate;
|
|
173
|
+
case "members-removed":
|
|
174
|
+
return handlers?.membersRemoved ?? handlers?.conversationUpdate;
|
|
175
|
+
case "channel-created":
|
|
176
|
+
return handlers?.channelCreated ?? handlers?.conversationUpdate;
|
|
177
|
+
case "channel-deleted":
|
|
178
|
+
return handlers?.channelDeleted ?? handlers?.conversationUpdate;
|
|
179
|
+
case "channel-renamed":
|
|
180
|
+
return handlers?.channelRenamed ?? handlers?.conversationUpdate;
|
|
181
|
+
case "channel-restored":
|
|
182
|
+
return handlers?.channelRestored ?? handlers?.conversationUpdate;
|
|
183
|
+
case "channel-shared":
|
|
184
|
+
return handlers?.channelShared ?? handlers?.conversationUpdate;
|
|
185
|
+
case "channel-unshared":
|
|
186
|
+
return handlers?.channelUnshared ?? handlers?.conversationUpdate;
|
|
187
|
+
case "team-renamed":
|
|
188
|
+
return handlers?.teamRenamed ?? handlers?.conversationUpdate;
|
|
189
|
+
case "team-archived":
|
|
190
|
+
return handlers?.teamArchived ?? handlers?.conversationUpdate;
|
|
191
|
+
case "team-unarchived":
|
|
192
|
+
return handlers?.teamUnarchived ?? handlers?.conversationUpdate;
|
|
193
|
+
case "team-deleted":
|
|
194
|
+
return handlers?.teamDeleted ?? handlers?.conversationUpdate;
|
|
195
|
+
case "team-hard-deleted":
|
|
196
|
+
return handlers?.teamHardDeleted ?? handlers?.conversationUpdate;
|
|
197
|
+
case "team-restored":
|
|
198
|
+
return handlers?.teamRestored ?? handlers?.conversationUpdate;
|
|
199
|
+
case "message-edit":
|
|
200
|
+
return handlers?.messageEdited;
|
|
201
|
+
case "message-delete":
|
|
202
|
+
return handlers?.messageDeleted;
|
|
203
|
+
case "message-restore":
|
|
204
|
+
return handlers?.messageRestored;
|
|
205
|
+
case "reaction":
|
|
206
|
+
return handlers?.reaction;
|
|
207
|
+
case "read-receipt":
|
|
208
|
+
return handlers?.readReceipt;
|
|
209
|
+
case "config-fetch":
|
|
210
|
+
return handlers?.configFetch ?? handlers?.invoke;
|
|
211
|
+
case "config-submit":
|
|
212
|
+
return handlers?.configSubmit ?? handlers?.invoke;
|
|
213
|
+
case "file-consent":
|
|
214
|
+
return handlers?.fileConsent ?? handlers?.invoke;
|
|
215
|
+
case "actionable-message-execute-action":
|
|
216
|
+
return handlers?.actionableMessageExecuteAction ?? handlers?.invoke;
|
|
217
|
+
case "adaptive-card-action":
|
|
218
|
+
return handlers?.adaptiveCardAction ?? handlers?.cardSubmit;
|
|
219
|
+
case "adaptive-card-search":
|
|
220
|
+
return handlers?.adaptiveCardSearch ?? handlers?.searchCommand;
|
|
221
|
+
case "feedback":
|
|
222
|
+
return handlers?.feedbackSubmit ?? handlers?.cardSubmit;
|
|
223
|
+
case "card-submit":
|
|
224
|
+
return handlers?.cardSubmit;
|
|
225
|
+
case "dialog-open":
|
|
226
|
+
return handlers?.dialogOpen;
|
|
227
|
+
case "dialog-submit":
|
|
228
|
+
return handlers?.dialogSubmit;
|
|
229
|
+
case "search-command":
|
|
230
|
+
return handlers?.searchCommand;
|
|
231
|
+
case "message-extension-query":
|
|
232
|
+
return handlers?.messageExtensionQuery ?? handlers?.searchCommand;
|
|
233
|
+
case "message-extension-query-link":
|
|
234
|
+
return handlers?.messageExtensionQueryLink ?? handlers?.searchCommand;
|
|
235
|
+
case "message-extension-anonymous-query-link":
|
|
236
|
+
return handlers?.messageExtensionAnonymousQueryLink ?? handlers?.searchCommand;
|
|
237
|
+
case "select-result":
|
|
238
|
+
return handlers?.selectResult;
|
|
239
|
+
case "message-extension-select-item":
|
|
240
|
+
return handlers?.messageExtensionSelectItem ?? handlers?.selectResult;
|
|
241
|
+
case "message-extension-submit-action":
|
|
242
|
+
return handlers?.messageExtensionSubmitAction ?? handlers?.cardSubmit;
|
|
243
|
+
case "message-extension-fetch-task":
|
|
244
|
+
return handlers?.messageExtensionFetchTask ?? handlers?.dialogOpen;
|
|
245
|
+
case "message-extension-query-setting-url":
|
|
246
|
+
return handlers?.messageExtensionQuerySettingUrl ?? handlers?.invoke;
|
|
247
|
+
case "message-extension-setting":
|
|
248
|
+
return handlers?.messageExtensionSetting ?? handlers?.invoke;
|
|
249
|
+
case "message-extension-card-button-clicked":
|
|
250
|
+
return handlers?.messageExtensionCardButtonClicked ?? handlers?.invoke;
|
|
251
|
+
case "task-fetch":
|
|
252
|
+
return handlers?.taskFetch ?? handlers?.dialogOpen;
|
|
253
|
+
case "task-submit":
|
|
254
|
+
return handlers?.taskSubmit ?? handlers?.dialogSubmit;
|
|
255
|
+
case "tab-fetch":
|
|
256
|
+
return handlers?.tabFetch ?? handlers?.invoke;
|
|
257
|
+
case "tab-submit":
|
|
258
|
+
return handlers?.tabSubmit ?? handlers?.invoke;
|
|
259
|
+
case "invoke":
|
|
260
|
+
return handlers?.invoke;
|
|
261
|
+
case "meeting-start":
|
|
262
|
+
return handlers?.meetingStart;
|
|
263
|
+
case "meeting-end":
|
|
264
|
+
return handlers?.meetingEnd;
|
|
265
|
+
case "participants-join":
|
|
266
|
+
return handlers?.participantsJoin;
|
|
267
|
+
case "participants-leave":
|
|
268
|
+
return handlers?.participantsLeave;
|
|
269
|
+
case "meeting-room-join":
|
|
270
|
+
return handlers?.meetingRoomJoin;
|
|
271
|
+
case "meeting-room-leave":
|
|
272
|
+
return handlers?.meetingRoomLeave;
|
|
273
|
+
case "meeting-stage-view":
|
|
274
|
+
return handlers?.meetingStageView;
|
|
275
|
+
case "meeting-smart-reply":
|
|
276
|
+
return handlers?.meetingSmartReply;
|
|
277
|
+
case "meeting-reaction":
|
|
278
|
+
return handlers?.meetingReaction;
|
|
279
|
+
case "meeting-poll-response":
|
|
280
|
+
return handlers?.meetingPollResponse;
|
|
281
|
+
case "meeting-apps-installed":
|
|
282
|
+
return handlers?.meetingAppsInstalled;
|
|
283
|
+
case "meeting-apps-uninstalled":
|
|
284
|
+
return handlers?.meetingAppsUninstalled;
|
|
285
|
+
case "meeting-recording-started":
|
|
286
|
+
return handlers?.meetingRecordingStarted;
|
|
287
|
+
case "meeting-recording-stopped":
|
|
288
|
+
return handlers?.meetingRecordingStopped;
|
|
289
|
+
case "meeting-focus-change":
|
|
290
|
+
return handlers?.meetingFocusChange;
|
|
291
|
+
case "meeting-screen-share-start":
|
|
292
|
+
return handlers?.meetingScreenShareStart;
|
|
293
|
+
case "meeting-screen-share-stop":
|
|
294
|
+
return handlers?.meetingScreenShareStop;
|
|
295
|
+
default:
|
|
296
|
+
return void 0;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function isTeamsInvokeResult(value) {
|
|
300
|
+
return !!value && typeof value === "object" && "status" in value;
|
|
301
|
+
}
|
|
302
|
+
function getInvokeResponseSent(context) {
|
|
303
|
+
return context.__invokeResponseSent === true;
|
|
304
|
+
}
|
|
305
|
+
function setInvokeResponseSent(context, value) {
|
|
306
|
+
context.__invokeResponseSent = value;
|
|
307
|
+
}
|
|
308
|
+
function resolveSource(options) {
|
|
309
|
+
if (options.source) return options.source;
|
|
310
|
+
if (options.agent) return options.agent;
|
|
311
|
+
throw new Error(
|
|
312
|
+
"Provide exactly one of TeamsChannelOptions.agent or TeamsChannelOptions.source."
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
function runAgentWithTeamsContext(ambient, source, sessionId, message, options) {
|
|
316
|
+
return streamAgentWithTeamsContext(
|
|
317
|
+
ambient,
|
|
318
|
+
source,
|
|
319
|
+
sessionId,
|
|
320
|
+
message,
|
|
321
|
+
options
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
async function* streamAgentWithTeamsContext(ambient, source, sessionId, message, options) {
|
|
325
|
+
const contextValue = {
|
|
326
|
+
...ambient,
|
|
327
|
+
message,
|
|
328
|
+
context: {
|
|
329
|
+
...ambient.context ?? {},
|
|
330
|
+
teams: ambient.teams
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
const iterator = await runWithM365TurnContext(
|
|
334
|
+
contextValue,
|
|
335
|
+
async () => source.chat(sessionId, message, options)[Symbol.asyncIterator]()
|
|
336
|
+
);
|
|
337
|
+
let completed = false;
|
|
338
|
+
try {
|
|
339
|
+
while (true) {
|
|
340
|
+
const result = await runWithM365TurnContext(
|
|
341
|
+
contextValue,
|
|
342
|
+
() => iterator.next()
|
|
343
|
+
);
|
|
344
|
+
if (result.done) {
|
|
345
|
+
completed = true;
|
|
346
|
+
return result.value;
|
|
347
|
+
}
|
|
348
|
+
yield result.value;
|
|
349
|
+
}
|
|
350
|
+
} finally {
|
|
351
|
+
if (!completed && iterator.return) {
|
|
352
|
+
await runWithM365TurnContext(
|
|
353
|
+
contextValue,
|
|
354
|
+
() => iterator.return(void 0)
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/context.ts
|
|
361
|
+
import {
|
|
362
|
+
currentM365TurnContext,
|
|
363
|
+
runWithM365TurnContext as runWithM365TurnContext2
|
|
364
|
+
} from "@cuylabs/channel-m365-agent-core";
|
|
365
|
+
function readTeamsContext(context) {
|
|
366
|
+
const payload = context?.context;
|
|
367
|
+
if (!payload || typeof payload !== "object") return void 0;
|
|
368
|
+
const teams = payload.teams;
|
|
369
|
+
return teams && typeof teams === "object" ? teams : void 0;
|
|
370
|
+
}
|
|
371
|
+
function currentTeamsTurnContext() {
|
|
372
|
+
const current = currentM365TurnContext();
|
|
373
|
+
const teams = readTeamsContext(current);
|
|
374
|
+
if (!current || !teams) return void 0;
|
|
375
|
+
return {
|
|
376
|
+
...current,
|
|
377
|
+
teams
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function runWithTeamsTurnContext(value, fn) {
|
|
381
|
+
return runWithM365TurnContext2(
|
|
382
|
+
{
|
|
383
|
+
...value,
|
|
384
|
+
context: {
|
|
385
|
+
...value.context ?? {},
|
|
386
|
+
teams: value.teams
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
fn
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/express.ts
|
|
394
|
+
import {
|
|
395
|
+
adaptJwtMiddleware,
|
|
396
|
+
processWithAdapter
|
|
397
|
+
} from "@cuylabs/channel-m365-agent-core/express-compat";
|
|
398
|
+
async function mountTeamsAgent(source, options = {}) {
|
|
399
|
+
const expressModule = await import("express");
|
|
400
|
+
const hostingModule = await import("@microsoft/agents-hosting");
|
|
401
|
+
const {
|
|
402
|
+
path: routePath = "/api/messages",
|
|
403
|
+
authConfig: providedAuthConfig,
|
|
404
|
+
port: portOption,
|
|
405
|
+
host,
|
|
406
|
+
adapter: providedAdapter,
|
|
407
|
+
app: providedApp,
|
|
408
|
+
...adapterOptions
|
|
409
|
+
} = options;
|
|
410
|
+
const authConfig = hostingModule.getAuthConfigWithDefaults(providedAuthConfig);
|
|
411
|
+
const cloudAdapter = providedAdapter ?? new hostingModule.CloudAdapter(authConfig);
|
|
412
|
+
const expressApp = providedApp ?? expressModule.default();
|
|
413
|
+
const channelAdapter = createTeamsChannelAdapter({
|
|
414
|
+
source,
|
|
415
|
+
...adapterOptions
|
|
416
|
+
});
|
|
417
|
+
const jsonParser = expressModule.json();
|
|
418
|
+
const jwtAuthorizer = adaptJwtMiddleware(hostingModule, authConfig);
|
|
419
|
+
expressApp.post(
|
|
420
|
+
routePath,
|
|
421
|
+
jsonParser,
|
|
422
|
+
jwtAuthorizer,
|
|
423
|
+
(req, res) => processWithAdapter(
|
|
424
|
+
cloudAdapter,
|
|
425
|
+
req,
|
|
426
|
+
res,
|
|
427
|
+
(context) => channelAdapter.handler(context)
|
|
428
|
+
)
|
|
429
|
+
);
|
|
430
|
+
const port = portOption === void 0 ? resolvePort(process.env.PORT) : portOption;
|
|
431
|
+
let server;
|
|
432
|
+
if (port !== null) {
|
|
433
|
+
const onListen = () => {
|
|
434
|
+
process.stdout.write(
|
|
435
|
+
`Teams agent listening on port ${port} \u2014 appId: ${authConfig.clientId ?? "(not set)"}
|
|
436
|
+
`
|
|
437
|
+
);
|
|
438
|
+
};
|
|
439
|
+
server = host ? expressApp.listen(port, host, onListen) : expressApp.listen(port, onListen);
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
app: expressApp,
|
|
443
|
+
channelAdapter,
|
|
444
|
+
cloudAdapter,
|
|
445
|
+
server
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function resolvePort(port) {
|
|
449
|
+
if (!port) return 3978;
|
|
450
|
+
const parsed = Number(port);
|
|
451
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
452
|
+
throw new Error(`Invalid PORT value: ${port}`);
|
|
453
|
+
}
|
|
454
|
+
return parsed;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/interactive-requests.ts
|
|
458
|
+
var TEAMS_INPUT_REQUEST_APPROVAL_VERB = "cuylabs.agent.input.approval";
|
|
459
|
+
var TEAMS_INPUT_REQUEST_HUMAN_VERB = "cuylabs.agent.input.human";
|
|
460
|
+
var ADAPTIVE_CARD_CONTENT_TYPE = "application/vnd.microsoft.card.adaptive";
|
|
461
|
+
var DEFAULT_MAX_ARGUMENT_CHARS = 1200;
|
|
462
|
+
function createTeamsApprovalRequestCard(input, options = {}) {
|
|
463
|
+
const { request, sessionId, turnId } = normalizeInputRequest(input);
|
|
464
|
+
const body = [
|
|
465
|
+
{
|
|
466
|
+
type: "TextBlock",
|
|
467
|
+
size: "Medium",
|
|
468
|
+
weight: "Bolder",
|
|
469
|
+
text: options.title ?? "Approval required",
|
|
470
|
+
wrap: true
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
type: "FactSet",
|
|
474
|
+
facts: [
|
|
475
|
+
{ title: "Tool", value: request.tool },
|
|
476
|
+
{ title: "Risk", value: request.risk }
|
|
477
|
+
]
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
type: "TextBlock",
|
|
481
|
+
text: request.description,
|
|
482
|
+
wrap: true
|
|
483
|
+
}
|
|
484
|
+
];
|
|
485
|
+
if (options.includeArguments ?? true) {
|
|
486
|
+
const formattedArgs = formatToolArguments(
|
|
487
|
+
request.args,
|
|
488
|
+
options.maxArgumentChars ?? DEFAULT_MAX_ARGUMENT_CHARS
|
|
489
|
+
);
|
|
490
|
+
if (formattedArgs) {
|
|
491
|
+
body.push({
|
|
492
|
+
type: "TextBlock",
|
|
493
|
+
text: formattedArgs,
|
|
494
|
+
wrap: true,
|
|
495
|
+
fontType: "Monospace",
|
|
496
|
+
isSubtle: true
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const rememberScopes = normalizeRememberScopes(request);
|
|
501
|
+
if (rememberScopes.length > 0) {
|
|
502
|
+
body.push({
|
|
503
|
+
type: "Input.ChoiceSet",
|
|
504
|
+
id: "rememberScope",
|
|
505
|
+
label: "Remember scope",
|
|
506
|
+
style: "compact",
|
|
507
|
+
value: request.defaultRememberScope ?? rememberScopes[0],
|
|
508
|
+
choices: rememberScopes.map((scope) => ({
|
|
509
|
+
title: labelRememberScope(scope),
|
|
510
|
+
value: scope
|
|
511
|
+
}))
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
body.push({
|
|
515
|
+
type: "Input.Text",
|
|
516
|
+
id: "feedback",
|
|
517
|
+
label: "Feedback",
|
|
518
|
+
isMultiline: true,
|
|
519
|
+
isRequired: false,
|
|
520
|
+
placeholder: "Optional"
|
|
521
|
+
});
|
|
522
|
+
const baseData = {
|
|
523
|
+
requestId: request.id,
|
|
524
|
+
sessionId,
|
|
525
|
+
...turnId ? { turnId } : {}
|
|
526
|
+
};
|
|
527
|
+
const actions = [
|
|
528
|
+
{
|
|
529
|
+
type: "Action.Submit",
|
|
530
|
+
title: "Approve",
|
|
531
|
+
data: {
|
|
532
|
+
...baseData,
|
|
533
|
+
verb: TEAMS_INPUT_REQUEST_APPROVAL_VERB,
|
|
534
|
+
action: "allow"
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
type: "Action.Submit",
|
|
539
|
+
title: "Deny",
|
|
540
|
+
data: {
|
|
541
|
+
...baseData,
|
|
542
|
+
verb: TEAMS_INPUT_REQUEST_APPROVAL_VERB,
|
|
543
|
+
action: "deny"
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
];
|
|
547
|
+
if (rememberScopes.length > 0) {
|
|
548
|
+
actions.push({
|
|
549
|
+
type: "Action.Submit",
|
|
550
|
+
title: "Remember",
|
|
551
|
+
data: {
|
|
552
|
+
...baseData,
|
|
553
|
+
verb: TEAMS_INPUT_REQUEST_APPROVAL_VERB,
|
|
554
|
+
action: "remember"
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
return createAdaptiveCardAttachment(body, actions);
|
|
559
|
+
}
|
|
560
|
+
function createTeamsHumanInputRequestCard(input, options = {}) {
|
|
561
|
+
const { request, sessionId, turnId } = normalizeInputRequest(input);
|
|
562
|
+
const body = [
|
|
563
|
+
{
|
|
564
|
+
type: "TextBlock",
|
|
565
|
+
size: "Medium",
|
|
566
|
+
weight: "Bolder",
|
|
567
|
+
text: options.title ?? request.title,
|
|
568
|
+
wrap: true
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
type: "TextBlock",
|
|
572
|
+
text: request.question,
|
|
573
|
+
wrap: true
|
|
574
|
+
}
|
|
575
|
+
];
|
|
576
|
+
const baseData = {
|
|
577
|
+
requestId: request.id,
|
|
578
|
+
sessionId,
|
|
579
|
+
...turnId ? { turnId } : {}
|
|
580
|
+
};
|
|
581
|
+
if (request.kind === "choice") {
|
|
582
|
+
body.push({
|
|
583
|
+
type: "Input.ChoiceSet",
|
|
584
|
+
id: "selected",
|
|
585
|
+
label: request.title,
|
|
586
|
+
style: request.allowMultiple ? "expanded" : "compact",
|
|
587
|
+
isMultiSelect: request.allowMultiple ?? false,
|
|
588
|
+
choices: (request.options ?? []).map((option) => ({
|
|
589
|
+
title: option.label,
|
|
590
|
+
value: option.value ?? option.label
|
|
591
|
+
}))
|
|
592
|
+
});
|
|
593
|
+
} else if (request.kind === "text") {
|
|
594
|
+
body.push({
|
|
595
|
+
type: "Input.Text",
|
|
596
|
+
id: "text",
|
|
597
|
+
label: request.title,
|
|
598
|
+
isMultiline: true,
|
|
599
|
+
placeholder: request.placeholder ?? "Type a response"
|
|
600
|
+
});
|
|
601
|
+
} else {
|
|
602
|
+
body.push({
|
|
603
|
+
type: "Input.Text",
|
|
604
|
+
id: "text",
|
|
605
|
+
label: "Details",
|
|
606
|
+
isMultiline: true,
|
|
607
|
+
isRequired: false,
|
|
608
|
+
placeholder: request.placeholder ?? "Optional"
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
const actions = request.kind === "confirm" ? [
|
|
612
|
+
{
|
|
613
|
+
type: "Action.Submit",
|
|
614
|
+
title: request.confirmLabel ?? "Confirm",
|
|
615
|
+
data: {
|
|
616
|
+
...baseData,
|
|
617
|
+
verb: TEAMS_INPUT_REQUEST_HUMAN_VERB,
|
|
618
|
+
responseKind: "confirm",
|
|
619
|
+
confirmed: true
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
type: "Action.Submit",
|
|
624
|
+
title: request.denyLabel ?? "Cancel",
|
|
625
|
+
data: {
|
|
626
|
+
...baseData,
|
|
627
|
+
verb: TEAMS_INPUT_REQUEST_HUMAN_VERB,
|
|
628
|
+
responseKind: "confirm",
|
|
629
|
+
confirmed: false
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
] : [
|
|
633
|
+
{
|
|
634
|
+
type: "Action.Submit",
|
|
635
|
+
title: request.confirmLabel ?? "Submit",
|
|
636
|
+
data: {
|
|
637
|
+
...baseData,
|
|
638
|
+
verb: TEAMS_INPUT_REQUEST_HUMAN_VERB,
|
|
639
|
+
responseKind: request.kind
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
];
|
|
643
|
+
return createAdaptiveCardAttachment(body, actions);
|
|
644
|
+
}
|
|
645
|
+
function parseTeamsInputRequestSubmit(value) {
|
|
646
|
+
const data = readSubmitData(value);
|
|
647
|
+
const verb = readString(data, "verb");
|
|
648
|
+
const requestId = readString(data, "requestId");
|
|
649
|
+
if (!requestId || verb !== TEAMS_INPUT_REQUEST_APPROVAL_VERB && verb !== TEAMS_INPUT_REQUEST_HUMAN_VERB) {
|
|
650
|
+
return void 0;
|
|
651
|
+
}
|
|
652
|
+
const sessionId = readString(data, "sessionId");
|
|
653
|
+
const turnId = readString(data, "turnId");
|
|
654
|
+
if (verb === TEAMS_INPUT_REQUEST_APPROVAL_VERB) {
|
|
655
|
+
const action = readApprovalAction(data);
|
|
656
|
+
if (!action) {
|
|
657
|
+
return void 0;
|
|
658
|
+
}
|
|
659
|
+
const feedback = readTrimmedString(data, "feedback");
|
|
660
|
+
const rememberScope = action === "remember" ? readRememberScope(data) : void 0;
|
|
661
|
+
return {
|
|
662
|
+
verb,
|
|
663
|
+
requestId,
|
|
664
|
+
...sessionId ? { sessionId } : {},
|
|
665
|
+
...turnId ? { turnId } : {},
|
|
666
|
+
payload: {
|
|
667
|
+
kind: "approval",
|
|
668
|
+
action,
|
|
669
|
+
...feedback ? { feedback } : {},
|
|
670
|
+
...rememberScope ? { rememberScope } : {}
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
const response = readHumanInputResponse(data);
|
|
675
|
+
if (!response) {
|
|
676
|
+
return void 0;
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
verb,
|
|
680
|
+
requestId,
|
|
681
|
+
...sessionId ? { sessionId } : {},
|
|
682
|
+
...turnId ? { turnId } : {},
|
|
683
|
+
payload: {
|
|
684
|
+
kind: "human",
|
|
685
|
+
response
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
function createAdaptiveCardAttachment(body, actions) {
|
|
690
|
+
return {
|
|
691
|
+
contentType: ADAPTIVE_CARD_CONTENT_TYPE,
|
|
692
|
+
content: {
|
|
693
|
+
type: "AdaptiveCard",
|
|
694
|
+
version: "1.5",
|
|
695
|
+
body,
|
|
696
|
+
actions
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function normalizeInputRequest(input) {
|
|
701
|
+
if ("request" in input) {
|
|
702
|
+
return {
|
|
703
|
+
request: input.request,
|
|
704
|
+
sessionId: input.sessionId ?? input.request.sessionId,
|
|
705
|
+
...input.turnId ? { turnId: input.turnId } : {}
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
request: input,
|
|
710
|
+
sessionId: input.sessionId
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function formatToolArguments(args, maxChars) {
|
|
714
|
+
let formatted;
|
|
715
|
+
try {
|
|
716
|
+
formatted = JSON.stringify(args, null, 2);
|
|
717
|
+
} catch {
|
|
718
|
+
return "";
|
|
719
|
+
}
|
|
720
|
+
if (!formatted || formatted === "undefined") {
|
|
721
|
+
return "";
|
|
722
|
+
}
|
|
723
|
+
if (formatted.length <= maxChars) {
|
|
724
|
+
return formatted;
|
|
725
|
+
}
|
|
726
|
+
return `${formatted.slice(0, Math.max(0, maxChars - 1))}...`;
|
|
727
|
+
}
|
|
728
|
+
function normalizeRememberScopes(request) {
|
|
729
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
730
|
+
"session",
|
|
731
|
+
"project",
|
|
732
|
+
"user"
|
|
733
|
+
]);
|
|
734
|
+
return (request.rememberScopes ?? []).filter((scope) => allowed.has(scope));
|
|
735
|
+
}
|
|
736
|
+
function labelRememberScope(scope) {
|
|
737
|
+
switch (scope) {
|
|
738
|
+
case "session":
|
|
739
|
+
return "This session";
|
|
740
|
+
case "project":
|
|
741
|
+
return "This project";
|
|
742
|
+
case "user":
|
|
743
|
+
return "My user";
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
function readSubmitData(value) {
|
|
747
|
+
const record = asRecord(value);
|
|
748
|
+
const nested = asRecord(record?.data);
|
|
749
|
+
return {
|
|
750
|
+
...nested ?? {},
|
|
751
|
+
...record ?? {}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
function readHumanInputResponse(data) {
|
|
755
|
+
const responseKind = readString(data, "responseKind");
|
|
756
|
+
switch (responseKind) {
|
|
757
|
+
case "text": {
|
|
758
|
+
const text = readString(data, "text") ?? "";
|
|
759
|
+
return { kind: "text", text };
|
|
760
|
+
}
|
|
761
|
+
case "confirm": {
|
|
762
|
+
const confirmed = readBoolean(data, "confirmed");
|
|
763
|
+
if (confirmed === void 0) {
|
|
764
|
+
return void 0;
|
|
765
|
+
}
|
|
766
|
+
const text = readString(data, "text") ?? (confirmed ? "confirmed" : "denied");
|
|
767
|
+
return { kind: "confirm", confirmed, text };
|
|
768
|
+
}
|
|
769
|
+
case "choice": {
|
|
770
|
+
const selected = readSelectedValues(data, "selected");
|
|
771
|
+
return { kind: "choice", selected, text: selected.join(", ") };
|
|
772
|
+
}
|
|
773
|
+
default:
|
|
774
|
+
return void 0;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
function readApprovalAction(data) {
|
|
778
|
+
const action = readString(data, "action");
|
|
779
|
+
return action === "allow" || action === "deny" || action === "remember" ? action : void 0;
|
|
780
|
+
}
|
|
781
|
+
function readRememberScope(data) {
|
|
782
|
+
const scope = readString(data, "rememberScope");
|
|
783
|
+
return scope === "session" || scope === "project" || scope === "user" ? scope : void 0;
|
|
784
|
+
}
|
|
785
|
+
function readSelectedValues(data, key) {
|
|
786
|
+
const value = data[key];
|
|
787
|
+
if (Array.isArray(value)) {
|
|
788
|
+
return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
789
|
+
}
|
|
790
|
+
if (typeof value === "string") {
|
|
791
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
792
|
+
}
|
|
793
|
+
return [];
|
|
794
|
+
}
|
|
795
|
+
function readTrimmedString(data, key) {
|
|
796
|
+
return readString(data, key)?.trim() || void 0;
|
|
797
|
+
}
|
|
798
|
+
function readString(data, key) {
|
|
799
|
+
const value = data?.[key];
|
|
800
|
+
return typeof value === "string" ? value : void 0;
|
|
801
|
+
}
|
|
802
|
+
function readBoolean(data, key) {
|
|
803
|
+
const value = data[key];
|
|
804
|
+
if (typeof value === "boolean") {
|
|
805
|
+
return value;
|
|
806
|
+
}
|
|
807
|
+
if (value === "true") {
|
|
808
|
+
return true;
|
|
809
|
+
}
|
|
810
|
+
if (value === "false") {
|
|
811
|
+
return false;
|
|
812
|
+
}
|
|
813
|
+
return void 0;
|
|
814
|
+
}
|
|
815
|
+
function asRecord(value) {
|
|
816
|
+
return value && typeof value === "object" ? value : void 0;
|
|
817
|
+
}
|
|
818
|
+
export {
|
|
819
|
+
Conversation,
|
|
820
|
+
ConversationBuilder,
|
|
821
|
+
ConversationReferenceBuilder,
|
|
822
|
+
CreateConversationOptionsBuilder,
|
|
823
|
+
TEAMS_INPUT_REQUEST_APPROVAL_VERB,
|
|
824
|
+
TEAMS_INPUT_REQUEST_HUMAN_VERB,
|
|
825
|
+
captureM365ConversationReference,
|
|
826
|
+
captureTeamsConversationReference,
|
|
827
|
+
continueM365Conversation,
|
|
828
|
+
createTeamsApprovalRequestCard,
|
|
829
|
+
createTeamsChannelAdapter,
|
|
830
|
+
createTeamsHumanInputRequestCard,
|
|
831
|
+
currentTeamsTurnContext,
|
|
832
|
+
mountTeamsAgent,
|
|
833
|
+
parseTeamsInputRequestSubmit,
|
|
834
|
+
restoreM365ConversationReference,
|
|
835
|
+
runWithTeamsTurnContext
|
|
836
|
+
};
|