@agenticmail/enterprise 0.5.316 → 0.5.318

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.
Files changed (36) hide show
  1. package/dist/agent-tools-263HM5QU.js +13949 -0
  2. package/dist/agent-tools-AT4D276V.js +13949 -0
  3. package/dist/chunk-36XNMIHA.js +5087 -0
  4. package/dist/chunk-6G5SXLXC.js +4921 -0
  5. package/dist/chunk-6PWDS7KY.js +5087 -0
  6. package/dist/chunk-D24JY75H.js +1519 -0
  7. package/dist/chunk-MMYBDHDB.js +4921 -0
  8. package/dist/chunk-OW4GLBHP.js +1519 -0
  9. package/dist/cli-agent-FNMDJN7T.js +2357 -0
  10. package/dist/cli-agent-ZSHUPBBN.js +2357 -0
  11. package/dist/cli-serve-DTQLN5UI.js +140 -0
  12. package/dist/cli-serve-LNTT73P2.js +140 -0
  13. package/dist/cli.js +3 -3
  14. package/dist/index.js +17 -17
  15. package/dist/integrations-TF4EBCJ7.js +43830 -0
  16. package/dist/microsoft-HPLA5ZL5.js +2414 -0
  17. package/dist/microsoft-UFLZBEAC.js +1619 -0
  18. package/dist/runtime-HTIM7GZR.js +45 -0
  19. package/dist/runtime-QQ6LAY4U.js +45 -0
  20. package/dist/server-DFR7FI3Q.js +28 -0
  21. package/dist/server-TXV3ZVVR.js +28 -0
  22. package/dist/setup-ADSKKBGV.js +20 -0
  23. package/dist/setup-LW4MLU2N.js +20 -0
  24. package/package.json +1 -1
  25. package/src/agent-tools/index.ts +7 -2
  26. package/src/agent-tools/tools/microsoft/contacts.ts +176 -0
  27. package/src/agent-tools/tools/microsoft/excel.ts +261 -0
  28. package/src/agent-tools/tools/microsoft/graph-api.ts +50 -0
  29. package/src/agent-tools/tools/microsoft/index.ts +79 -0
  30. package/src/agent-tools/tools/microsoft/onedrive.ts +228 -0
  31. package/src/agent-tools/tools/microsoft/onenote.ts +186 -0
  32. package/src/agent-tools/tools/microsoft/outlook-calendar.ts +286 -0
  33. package/src/agent-tools/tools/microsoft/outlook-mail.ts +431 -0
  34. package/src/agent-tools/tools/microsoft/sharepoint.ts +328 -0
  35. package/src/agent-tools/tools/microsoft/teams.ts +244 -0
  36. package/src/agent-tools/tools/microsoft/todo.ts +181 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Microsoft SharePoint Tools
3
+ *
4
+ * Site, list, document library, and page operations via Microsoft Graph API.
5
+ */
6
+
7
+ import type { AnyAgentTool, ToolCreationOptions } from '../../types.js';
8
+ import { jsonResult, errorResult } from '../../common.js';
9
+ import type { MicrosoftToolsConfig } from './index.js';
10
+ import { graph } from './graph-api.js';
11
+
12
+ export function createSharePointTools(config: MicrosoftToolsConfig, _options?: ToolCreationOptions): AnyAgentTool[] {
13
+ const tp = config.tokenProvider;
14
+
15
+ return [
16
+ {
17
+ name: 'sharepoint_list_sites',
18
+ description: 'Search or list SharePoint sites the agent has access to.',
19
+ category: 'utility' as const,
20
+ parameters: {
21
+ type: 'object' as const,
22
+ properties: {
23
+ search: { type: 'string', description: 'Search query for site name/description' },
24
+ maxResults: { type: 'number', description: 'Max results (default: 20)' },
25
+ },
26
+ required: [],
27
+ },
28
+ async execute(_id: string, params: any) {
29
+ try {
30
+ const token = await tp.getAccessToken();
31
+ let path = '/sites';
32
+ const query: Record<string, string> = { '$top': String(params.maxResults || 20) };
33
+ if (params.search) {
34
+ query['$search'] = `"${params.search}"`;
35
+ }
36
+ const data = await graph(token, path, { query });
37
+ const sites = (data.value || []).map((s: any) => ({
38
+ id: s.id, name: s.displayName, description: s.description,
39
+ webUrl: s.webUrl, created: s.createdDateTime,
40
+ }));
41
+ return jsonResult({ sites, count: sites.length });
42
+ } catch (e: any) { return errorResult(e.message); }
43
+ },
44
+ },
45
+
46
+ {
47
+ name: 'sharepoint_get_site',
48
+ description: 'Get details about a SharePoint site by hostname and path, or by site ID.',
49
+ category: 'utility' as const,
50
+ parameters: {
51
+ type: 'object' as const,
52
+ properties: {
53
+ siteId: { type: 'string', description: 'Site ID' },
54
+ hostname: { type: 'string', description: 'Site hostname (e.g., "contoso.sharepoint.com")' },
55
+ path: { type: 'string', description: 'Site path (e.g., "/sites/engineering")' },
56
+ },
57
+ required: [],
58
+ },
59
+ async execute(_id: string, params: any) {
60
+ try {
61
+ const token = await tp.getAccessToken();
62
+ let sitePath: string;
63
+ if (params.siteId) {
64
+ sitePath = `/sites/${params.siteId}`;
65
+ } else if (params.hostname) {
66
+ sitePath = `/sites/${params.hostname}:${params.path || '/'}`;
67
+ } else {
68
+ throw new Error('Provide siteId or hostname');
69
+ }
70
+ const site = await graph(token, sitePath);
71
+ return jsonResult({
72
+ id: site.id, name: site.displayName, description: site.description,
73
+ webUrl: site.webUrl, created: site.createdDateTime,
74
+ });
75
+ } catch (e: any) { return errorResult(e.message); }
76
+ },
77
+ },
78
+
79
+ {
80
+ name: 'sharepoint_list_drives',
81
+ description: 'List document libraries (drives) on a SharePoint site.',
82
+ category: 'utility' as const,
83
+ parameters: {
84
+ type: 'object' as const,
85
+ properties: {
86
+ siteId: { type: 'string', description: 'SharePoint site ID' },
87
+ },
88
+ required: ['siteId'],
89
+ },
90
+ async execute(_id: string, params: any) {
91
+ try {
92
+ const token = await tp.getAccessToken();
93
+ const data = await graph(token, `/sites/${params.siteId}/drives`);
94
+ const drives = (data.value || []).map((d: any) => ({
95
+ id: d.id, name: d.name, description: d.description,
96
+ driveType: d.driveType, webUrl: d.webUrl,
97
+ totalSize: d.quota?.total, usedSize: d.quota?.used,
98
+ }));
99
+ return jsonResult({ drives, count: drives.length });
100
+ } catch (e: any) { return errorResult(e.message); }
101
+ },
102
+ },
103
+
104
+ {
105
+ name: 'sharepoint_list_files',
106
+ description: 'List files and folders in a SharePoint document library.',
107
+ category: 'utility' as const,
108
+ parameters: {
109
+ type: 'object' as const,
110
+ properties: {
111
+ siteId: { type: 'string', description: 'SharePoint site ID' },
112
+ driveId: { type: 'string', description: 'Drive (document library) ID' },
113
+ path: { type: 'string', description: 'Folder path within the drive (default: root)' },
114
+ maxResults: { type: 'number', description: 'Max items (default: 50)' },
115
+ },
116
+ required: ['siteId', 'driveId'],
117
+ },
118
+ async execute(_id: string, params: any) {
119
+ try {
120
+ const token = await tp.getAccessToken();
121
+ const basePath = params.path
122
+ ? `/drives/${params.driveId}/root:${params.path}:/children`
123
+ : `/drives/${params.driveId}/root/children`;
124
+ const data = await graph(token, basePath, {
125
+ query: {
126
+ '$top': String(params.maxResults || 50),
127
+ '$select': 'id,name,size,createdDateTime,lastModifiedDateTime,webUrl,folder,file',
128
+ }
129
+ });
130
+ const items = (data.value || []).map((i: any) => ({
131
+ id: i.id, name: i.name, size: i.size,
132
+ type: i.folder ? 'folder' : 'file',
133
+ mimeType: i.file?.mimeType,
134
+ childCount: i.folder?.childCount,
135
+ created: i.createdDateTime,
136
+ modified: i.lastModifiedDateTime,
137
+ webUrl: i.webUrl,
138
+ }));
139
+ return jsonResult({ items, count: items.length });
140
+ } catch (e: any) { return errorResult(e.message); }
141
+ },
142
+ },
143
+
144
+ {
145
+ name: 'sharepoint_upload_file',
146
+ description: 'Upload a text file to a SharePoint document library.',
147
+ category: 'utility' as const,
148
+ parameters: {
149
+ type: 'object' as const,
150
+ properties: {
151
+ driveId: { type: 'string', description: 'Drive (document library) ID' },
152
+ path: { type: 'string', description: 'Destination path (e.g., "/General/report.md")' },
153
+ content: { type: 'string', description: 'File content (text)' },
154
+ },
155
+ required: ['driveId', 'path', 'content'],
156
+ },
157
+ async execute(_id: string, params: any) {
158
+ try {
159
+ const token = await tp.getAccessToken();
160
+ const res = await fetch(`https://graph.microsoft.com/v1.0/drives/${params.driveId}/root:${params.path}:/content`, {
161
+ method: 'PUT',
162
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'text/plain' },
163
+ body: params.content,
164
+ });
165
+ if (!res.ok) throw new Error(`Upload failed: ${res.status} ${await res.text()}`);
166
+ const data = await res.json();
167
+ return jsonResult({ id: data.id, name: data.name, size: data.size, webUrl: data.webUrl });
168
+ } catch (e: any) { return errorResult(e.message); }
169
+ },
170
+ },
171
+
172
+ {
173
+ name: 'sharepoint_list_lists',
174
+ description: 'List SharePoint lists on a site.',
175
+ category: 'utility' as const,
176
+ parameters: {
177
+ type: 'object' as const,
178
+ properties: {
179
+ siteId: { type: 'string', description: 'SharePoint site ID' },
180
+ },
181
+ required: ['siteId'],
182
+ },
183
+ async execute(_id: string, params: any) {
184
+ try {
185
+ const token = await tp.getAccessToken();
186
+ const data = await graph(token, `/sites/${params.siteId}/lists`, {
187
+ query: { '$select': 'id,displayName,description,webUrl,list', '$top': '50' }
188
+ });
189
+ const lists = (data.value || []).map((l: any) => ({
190
+ id: l.id, name: l.displayName, description: l.description,
191
+ webUrl: l.webUrl, template: l.list?.template,
192
+ hidden: l.list?.hidden,
193
+ }));
194
+ return jsonResult({ lists: lists.filter((l: any) => !l.hidden), count: lists.length });
195
+ } catch (e: any) { return errorResult(e.message); }
196
+ },
197
+ },
198
+
199
+ {
200
+ name: 'sharepoint_list_items',
201
+ description: 'Read items from a SharePoint list.',
202
+ category: 'utility' as const,
203
+ parameters: {
204
+ type: 'object' as const,
205
+ properties: {
206
+ siteId: { type: 'string', description: 'SharePoint site ID' },
207
+ listId: { type: 'string', description: 'List ID or name' },
208
+ maxResults: { type: 'number', description: 'Max items (default: 50)' },
209
+ filter: { type: 'string', description: 'OData $filter expression' },
210
+ expand: { type: 'boolean', description: 'Expand field values (default: true)' },
211
+ },
212
+ required: ['siteId', 'listId'],
213
+ },
214
+ async execute(_id: string, params: any) {
215
+ try {
216
+ const token = await tp.getAccessToken();
217
+ const query: Record<string, string> = {
218
+ '$top': String(params.maxResults || 50),
219
+ };
220
+ if (params.expand !== false) query['$expand'] = 'fields';
221
+ if (params.filter) query['$filter'] = params.filter;
222
+ const data = await graph(token, `/sites/${params.siteId}/lists/${params.listId}/items`, { query });
223
+ const items = (data.value || []).map((i: any) => ({
224
+ id: i.id,
225
+ created: i.createdDateTime,
226
+ modified: i.lastModifiedDateTime,
227
+ webUrl: i.webUrl,
228
+ fields: i.fields,
229
+ }));
230
+ return jsonResult({ items, count: items.length });
231
+ } catch (e: any) { return errorResult(e.message); }
232
+ },
233
+ },
234
+
235
+ {
236
+ name: 'sharepoint_create_list_item',
237
+ description: 'Create a new item in a SharePoint list.',
238
+ category: 'utility' as const,
239
+ parameters: {
240
+ type: 'object' as const,
241
+ properties: {
242
+ siteId: { type: 'string', description: 'SharePoint site ID' },
243
+ listId: { type: 'string', description: 'List ID or name' },
244
+ fields: { type: 'object', description: 'Field values as key-value pairs (e.g., {"Title": "My Item", "Status": "Active"})' },
245
+ },
246
+ required: ['siteId', 'listId', 'fields'],
247
+ },
248
+ async execute(_id: string, params: any) {
249
+ try {
250
+ const token = await tp.getAccessToken();
251
+ const item = await graph(token, `/sites/${params.siteId}/lists/${params.listId}/items`, {
252
+ method: 'POST',
253
+ body: { fields: params.fields },
254
+ });
255
+ return jsonResult({ id: item.id, created: true, fields: item.fields });
256
+ } catch (e: any) { return errorResult(e.message); }
257
+ },
258
+ },
259
+
260
+ {
261
+ name: 'sharepoint_update_list_item',
262
+ description: 'Update an existing item in a SharePoint list.',
263
+ category: 'utility' as const,
264
+ parameters: {
265
+ type: 'object' as const,
266
+ properties: {
267
+ siteId: { type: 'string', description: 'SharePoint site ID' },
268
+ listId: { type: 'string', description: 'List ID or name' },
269
+ itemId: { type: 'string', description: 'Item ID to update' },
270
+ fields: { type: 'object', description: 'Updated field values' },
271
+ },
272
+ required: ['siteId', 'listId', 'itemId', 'fields'],
273
+ },
274
+ async execute(_id: string, params: any) {
275
+ try {
276
+ const token = await tp.getAccessToken();
277
+ await graph(token, `/sites/${params.siteId}/lists/${params.listId}/items/${params.itemId}/fields`, {
278
+ method: 'PATCH',
279
+ body: params.fields,
280
+ });
281
+ return jsonResult({ updated: true, itemId: params.itemId });
282
+ } catch (e: any) { return errorResult(e.message); }
283
+ },
284
+ },
285
+
286
+ {
287
+ name: 'sharepoint_search',
288
+ description: 'Search across SharePoint for sites, files, list items, and pages.',
289
+ category: 'utility' as const,
290
+ parameters: {
291
+ type: 'object' as const,
292
+ properties: {
293
+ query: { type: 'string', description: 'Search query' },
294
+ entityTypes: { type: 'string', description: 'Comma-separated: site, drive, driveItem, list, listItem, message (default: driveItem)' },
295
+ maxResults: { type: 'number', description: 'Max results (default: 10)' },
296
+ },
297
+ required: ['query'],
298
+ },
299
+ async execute(_id: string, params: any) {
300
+ try {
301
+ const token = await tp.getAccessToken();
302
+ const types = (params.entityTypes || 'driveItem').split(',').map((t: string) => t.trim());
303
+ const data = await graph(token, '/search/query', {
304
+ method: 'POST',
305
+ body: {
306
+ requests: [{
307
+ entityTypes: types,
308
+ query: { queryString: params.query },
309
+ from: 0,
310
+ size: params.maxResults || 10,
311
+ }],
312
+ },
313
+ });
314
+ const hits = data.value?.[0]?.hitsContainers?.[0]?.hits || [];
315
+ const results = hits.map((h: any) => ({
316
+ id: h.resource?.id,
317
+ name: h.resource?.name || h.resource?.displayName,
318
+ summary: h.summary,
319
+ webUrl: h.resource?.webUrl,
320
+ type: h.resource?.['@odata.type'],
321
+ lastModified: h.resource?.lastModifiedDateTime,
322
+ }));
323
+ return jsonResult({ results, count: results.length, query: params.query });
324
+ } catch (e: any) { return errorResult(e.message); }
325
+ },
326
+ },
327
+ ];
328
+ }
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Microsoft Teams Tools
3
+ *
4
+ * Team/channel messaging, chat, and presence via Microsoft Graph API.
5
+ */
6
+
7
+ import type { AnyAgentTool, ToolCreationOptions } from '../../types.js';
8
+ import { jsonResult, errorResult } from '../../common.js';
9
+ import type { MicrosoftToolsConfig } from './index.js';
10
+ import { graph } from './graph-api.js';
11
+
12
+ export function createTeamsTools(config: MicrosoftToolsConfig, _options?: ToolCreationOptions): AnyAgentTool[] {
13
+ const tp = config.tokenProvider;
14
+
15
+ return [
16
+ {
17
+ name: 'teams_list_teams',
18
+ description: 'List all Teams the agent is a member of.',
19
+ category: 'utility' as const,
20
+ parameters: { type: 'object' as const, properties: {}, required: [] },
21
+ async execute(_id: string) {
22
+ try {
23
+ const token = await tp.getAccessToken();
24
+ const data = await graph(token, '/me/joinedTeams', {
25
+ query: { '$select': 'id,displayName,description' }
26
+ });
27
+ const teams = (data.value || []).map((t: any) => ({
28
+ id: t.id, name: t.displayName, description: t.description,
29
+ }));
30
+ return jsonResult({ teams, count: teams.length });
31
+ } catch (e: any) { return errorResult(e.message); }
32
+ },
33
+ },
34
+
35
+ {
36
+ name: 'teams_list_channels',
37
+ description: 'List channels in a Team.',
38
+ category: 'utility' as const,
39
+ parameters: {
40
+ type: 'object' as const,
41
+ properties: {
42
+ teamId: { type: 'string', description: 'Team ID' },
43
+ },
44
+ required: ['teamId'],
45
+ },
46
+ async execute(_id: string, params: any) {
47
+ try {
48
+ const token = await tp.getAccessToken();
49
+ const data = await graph(token, `/teams/${params.teamId}/channels`, {
50
+ query: { '$select': 'id,displayName,description,membershipType' }
51
+ });
52
+ const channels = (data.value || []).map((c: any) => ({
53
+ id: c.id, name: c.displayName, description: c.description,
54
+ membershipType: c.membershipType,
55
+ }));
56
+ return jsonResult({ channels, count: channels.length });
57
+ } catch (e: any) { return errorResult(e.message); }
58
+ },
59
+ },
60
+
61
+ {
62
+ name: 'teams_send_channel_message',
63
+ description: 'Send a message to a Teams channel.',
64
+ category: 'utility' as const,
65
+ parameters: {
66
+ type: 'object' as const,
67
+ properties: {
68
+ teamId: { type: 'string', description: 'Team ID' },
69
+ channelId: { type: 'string', description: 'Channel ID' },
70
+ message: { type: 'string', description: 'Message content (supports HTML)' },
71
+ isHtml: { type: 'boolean', description: 'Whether message is HTML (default: false)' },
72
+ },
73
+ required: ['teamId', 'channelId', 'message'],
74
+ },
75
+ async execute(_id: string, params: any) {
76
+ try {
77
+ const token = await tp.getAccessToken();
78
+ const msg = await graph(token, `/teams/${params.teamId}/channels/${params.channelId}/messages`, {
79
+ method: 'POST',
80
+ body: {
81
+ body: {
82
+ contentType: params.isHtml ? 'html' : 'text',
83
+ content: params.message,
84
+ },
85
+ },
86
+ });
87
+ return jsonResult({ id: msg.id, sent: true });
88
+ } catch (e: any) { return errorResult(e.message); }
89
+ },
90
+ },
91
+
92
+ {
93
+ name: 'teams_read_channel_messages',
94
+ description: 'Read recent messages from a Teams channel.',
95
+ category: 'utility' as const,
96
+ parameters: {
97
+ type: 'object' as const,
98
+ properties: {
99
+ teamId: { type: 'string', description: 'Team ID' },
100
+ channelId: { type: 'string', description: 'Channel ID' },
101
+ maxResults: { type: 'number', description: 'Max messages (default: 20)' },
102
+ },
103
+ required: ['teamId', 'channelId'],
104
+ },
105
+ async execute(_id: string, params: any) {
106
+ try {
107
+ const token = await tp.getAccessToken();
108
+ const data = await graph(token, `/teams/${params.teamId}/channels/${params.channelId}/messages`, {
109
+ query: { '$top': String(params.maxResults || 20) }
110
+ });
111
+ const messages = (data.value || []).map((m: any) => ({
112
+ id: m.id,
113
+ from: m.from?.user?.displayName || m.from?.application?.displayName,
114
+ fromEmail: m.from?.user?.email,
115
+ body: m.body?.content,
116
+ bodyType: m.body?.contentType,
117
+ date: m.createdDateTime,
118
+ importance: m.importance,
119
+ replyCount: m.replies?.length,
120
+ }));
121
+ return jsonResult({ messages, count: messages.length });
122
+ } catch (e: any) { return errorResult(e.message); }
123
+ },
124
+ },
125
+
126
+ {
127
+ name: 'teams_list_chats',
128
+ description: 'List the agent\'s 1:1 and group chats in Teams.',
129
+ category: 'utility' as const,
130
+ parameters: {
131
+ type: 'object' as const,
132
+ properties: {
133
+ maxResults: { type: 'number', description: 'Max chats to return (default: 20)' },
134
+ },
135
+ required: [],
136
+ },
137
+ async execute(_id: string, params: any) {
138
+ try {
139
+ const token = await tp.getAccessToken();
140
+ const data = await graph(token, '/me/chats', {
141
+ query: {
142
+ '$top': String(params.maxResults || 20),
143
+ '$select': 'id,topic,chatType,createdDateTime,lastUpdatedDateTime',
144
+ '$orderby': 'lastUpdatedDateTime desc',
145
+ }
146
+ });
147
+ const chats = (data.value || []).map((c: any) => ({
148
+ id: c.id, topic: c.topic, type: c.chatType,
149
+ created: c.createdDateTime, lastUpdated: c.lastUpdatedDateTime,
150
+ }));
151
+ return jsonResult({ chats, count: chats.length });
152
+ } catch (e: any) { return errorResult(e.message); }
153
+ },
154
+ },
155
+
156
+ {
157
+ name: 'teams_send_chat_message',
158
+ description: 'Send a message in a Teams 1:1 or group chat.',
159
+ category: 'utility' as const,
160
+ parameters: {
161
+ type: 'object' as const,
162
+ properties: {
163
+ chatId: { type: 'string', description: 'Chat ID' },
164
+ message: { type: 'string', description: 'Message content' },
165
+ isHtml: { type: 'boolean', description: 'Whether message is HTML (default: false)' },
166
+ },
167
+ required: ['chatId', 'message'],
168
+ },
169
+ async execute(_id: string, params: any) {
170
+ try {
171
+ const token = await tp.getAccessToken();
172
+ const msg = await graph(token, `/chats/${params.chatId}/messages`, {
173
+ method: 'POST',
174
+ body: {
175
+ body: { contentType: params.isHtml ? 'html' : 'text', content: params.message },
176
+ },
177
+ });
178
+ return jsonResult({ id: msg.id, sent: true });
179
+ } catch (e: any) { return errorResult(e.message); }
180
+ },
181
+ },
182
+
183
+ {
184
+ name: 'teams_read_chat_messages',
185
+ description: 'Read recent messages from a Teams chat.',
186
+ category: 'utility' as const,
187
+ parameters: {
188
+ type: 'object' as const,
189
+ properties: {
190
+ chatId: { type: 'string', description: 'Chat ID' },
191
+ maxResults: { type: 'number', description: 'Max messages (default: 20)' },
192
+ },
193
+ required: ['chatId'],
194
+ },
195
+ async execute(_id: string, params: any) {
196
+ try {
197
+ const token = await tp.getAccessToken();
198
+ const data = await graph(token, `/chats/${params.chatId}/messages`, {
199
+ query: { '$top': String(params.maxResults || 20), '$orderby': 'createdDateTime desc' }
200
+ });
201
+ const messages = (data.value || []).map((m: any) => ({
202
+ id: m.id,
203
+ from: m.from?.user?.displayName,
204
+ body: m.body?.content,
205
+ bodyType: m.body?.contentType,
206
+ date: m.createdDateTime,
207
+ }));
208
+ return jsonResult({ messages, count: messages.length });
209
+ } catch (e: any) { return errorResult(e.message); }
210
+ },
211
+ },
212
+
213
+ {
214
+ name: 'teams_presence',
215
+ description: 'Get presence/availability status for users.',
216
+ category: 'utility' as const,
217
+ parameters: {
218
+ type: 'object' as const,
219
+ properties: {
220
+ userIds: { type: 'string', description: 'User IDs, comma-separated (use "me" for self)' },
221
+ },
222
+ required: ['userIds'],
223
+ },
224
+ async execute(_id: string, params: any) {
225
+ try {
226
+ const token = await tp.getAccessToken();
227
+ if (params.userIds === 'me') {
228
+ const p = await graph(token, '/me/presence');
229
+ return jsonResult({ presence: [{ availability: p.availability, activity: p.activity }] });
230
+ }
231
+ const ids = params.userIds.split(',').map((s: string) => s.trim());
232
+ const data = await graph(token, '/communications/getPresencesByUserId', {
233
+ method: 'POST',
234
+ body: { ids },
235
+ });
236
+ const presence = (data.value || []).map((p: any) => ({
237
+ userId: p.id, availability: p.availability, activity: p.activity,
238
+ }));
239
+ return jsonResult({ presence });
240
+ } catch (e: any) { return errorResult(e.message); }
241
+ },
242
+ },
243
+ ];
244
+ }