@littlebearapps/outlook-assistant 3.13.0 → 3.14.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/.env.example +27 -3
- package/README.md +66 -26
- package/advanced/index.js +44 -174
- package/auth/auth-errors.js +23 -1
- package/auth/oauth-server.js +7 -1
- package/auth/token-manager.js +7 -3
- package/auth/token-storage.js +28 -30
- package/auth/tools.js +45 -76
- package/calendar/attendees.js +36 -0
- package/calendar/cancel.js +9 -25
- package/calendar/create.js +42 -48
- package/calendar/decline.js +10 -25
- package/calendar/delete.js +10 -25
- package/calendar/index.js +20 -37
- package/calendar/list.js +4 -16
- package/calendar/preview.js +335 -0
- package/calendar/update.js +42 -86
- package/categories/index.js +59 -264
- package/config.js +29 -1
- package/contacts/index.js +72 -128
- package/email/attachments.js +42 -124
- package/email/conversations.js +44 -78
- package/email/delta.js +10 -34
- package/email/draft.js +140 -96
- package/email/export.js +141 -110
- package/email/folder-utils.js +3 -2
- package/email/headers.js +11 -49
- package/email/index.js +85 -109
- package/email/list.js +4 -17
- package/email/mail-tips.js +86 -57
- package/email/mark-as-read.js +13 -49
- package/email/mime.js +14 -49
- package/email/read.js +16 -50
- package/email/search.js +46 -86
- package/email/send.js +82 -48
- package/folder/create.js +6 -25
- package/folder/delete.js +117 -38
- package/folder/index.js +17 -16
- package/folder/list.js +5 -17
- package/folder/move.js +13 -42
- package/folder/resolve.js +11 -6
- package/folder/stats.js +6 -20
- package/index.js +19 -43
- package/llms-install.md +22 -4
- package/llms.txt +17 -8
- package/outlook-auth-server.js +10 -3
- package/package.json +4 -1
- package/request-handler.js +217 -116
- package/rules/create.js +27 -70
- package/rules/index.js +30 -92
- package/rules/list.js +5 -17
- package/rules/rule-builder.js +57 -20
- package/rules/update.js +26 -60
- package/server.js +37 -0
- package/settings/index.js +142 -143
- package/tools.js +30 -0
- package/utils/field-presets.js +4 -2
- package/utils/graph-api.js +65 -22
- package/utils/logger.js +251 -0
- package/utils/mock-data.js +91 -2
- package/utils/read-only.js +59 -0
- package/utils/response-formatter.js +54 -15
- package/utils/risk-classes.js +324 -0
- package/utils/safe-write.js +372 -6
- package/utils/safety.js +109 -25
- package/utils/server-instructions.js +62 -0
- package/utils/tool-error.js +33 -0
package/email/mail-tips.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
8
8
|
const { ensureAuthenticated } = require('../auth');
|
|
9
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* All available mail tip types from Microsoft Graph API
|
|
@@ -25,17 +26,27 @@ const MAIL_TIP_TYPES = [
|
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Format mail tips into readable markdown
|
|
29
|
+
*
|
|
30
|
+
* Graph's mailTips resource reports `mailboxFull`, `deliveryRestricted` and
|
|
31
|
+
* `isModerated` as booleans. The older `mailboxFullStatus`,
|
|
32
|
+
* `deliveryRestriction` and `moderationStatus` shapes are still recognised.
|
|
28
33
|
* @param {Array} mailTips - Array of mail tip objects from Graph API
|
|
29
|
-
* @returns {string
|
|
34
|
+
* @returns {{formatted: string, warningCount: number, issues: Array<{address: string, type: string}>}}
|
|
35
|
+
* `issues` lists each flagged condition per recipient, with `type` one of
|
|
36
|
+
* outOfOffice, mailboxFull, customTip, deliveryRestricted, moderated,
|
|
37
|
+
* external or externalMembers. Every issue is also a warning in the text,
|
|
38
|
+
* so the text never shows ✓ for a recipient send-email would refuse
|
|
30
39
|
*/
|
|
31
40
|
function formatMailTips(mailTips) {
|
|
32
41
|
const lines = [];
|
|
42
|
+
const issues = [];
|
|
33
43
|
let warningCount = 0;
|
|
34
44
|
|
|
35
45
|
for (const tip of mailTips) {
|
|
36
46
|
const email = tip.emailAddress?.address || 'Unknown';
|
|
37
47
|
const tipLines = [];
|
|
38
48
|
const warnings = [];
|
|
49
|
+
const flag = (type) => issues.push({ address: email, type });
|
|
39
50
|
|
|
40
51
|
// Out-of-office / automatic replies
|
|
41
52
|
if (
|
|
@@ -43,6 +54,7 @@ function formatMailTips(mailTips) {
|
|
|
43
54
|
tip.automaticReplies.message.trim() !== ''
|
|
44
55
|
) {
|
|
45
56
|
warnings.push('Out of Office');
|
|
57
|
+
flag('outOfOffice');
|
|
46
58
|
const reply = tip.automaticReplies;
|
|
47
59
|
tipLines.push(
|
|
48
60
|
` **Out of Office**: ${reply.message.replace(/\n/g, ' ').substring(0, 200)}`
|
|
@@ -57,8 +69,9 @@ function formatMailTips(mailTips) {
|
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
// Mailbox full
|
|
60
|
-
if (tip.mailboxFullStatus) {
|
|
72
|
+
if (tip.mailboxFull === true || tip.mailboxFullStatus === true) {
|
|
61
73
|
warnings.push('Mailbox Full');
|
|
74
|
+
flag('mailboxFull');
|
|
62
75
|
tipLines.push(
|
|
63
76
|
` **Mailbox Full**: Recipient's mailbox is full — delivery may fail`
|
|
64
77
|
);
|
|
@@ -67,30 +80,38 @@ function formatMailTips(mailTips) {
|
|
|
67
80
|
// Custom mail tip (admin-configured)
|
|
68
81
|
if (tip.customMailTip) {
|
|
69
82
|
warnings.push('Custom Tip');
|
|
83
|
+
flag('customTip');
|
|
70
84
|
tipLines.push(` **Notice**: ${tip.customMailTip}`);
|
|
71
85
|
}
|
|
72
86
|
|
|
73
87
|
// Delivery restriction
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
if (
|
|
89
|
+
tip.deliveryRestricted === true ||
|
|
90
|
+
tip.deliveryRestriction?.isDeliveryRestricted
|
|
91
|
+
) {
|
|
92
|
+
warnings.push('Delivery Restricted');
|
|
93
|
+
flag('deliveryRestricted');
|
|
94
|
+
tipLines.push(
|
|
95
|
+
` **Delivery Restricted**: ${tip.deliveryRestriction?.message || 'Cannot deliver to this recipient'}`
|
|
96
|
+
);
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
// Moderation status
|
|
85
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
tip.isModerated === true ||
|
|
102
|
+
(tip.moderationStatus && tip.moderationStatus !== 'notModerated')
|
|
103
|
+
) {
|
|
86
104
|
warnings.push('Moderated');
|
|
105
|
+
flag('moderated');
|
|
87
106
|
tipLines.push(
|
|
88
107
|
` **Moderated**: Messages to this recipient require approval`
|
|
89
108
|
);
|
|
90
109
|
}
|
|
91
110
|
|
|
92
|
-
// Recipient scope
|
|
93
|
-
if (tip.recipientScope
|
|
111
|
+
// Recipient scope: external, externalPartner or externalNonPartner
|
|
112
|
+
if (/^external/i.test(tip.recipientScope || '')) {
|
|
113
|
+
warnings.push('External');
|
|
114
|
+
flag('external');
|
|
94
115
|
tipLines.push(` **External**: Recipient is outside your organisation`);
|
|
95
116
|
}
|
|
96
117
|
|
|
@@ -101,12 +122,27 @@ function formatMailTips(mailTips) {
|
|
|
101
122
|
}
|
|
102
123
|
|
|
103
124
|
// Group member counts
|
|
104
|
-
if (tip.
|
|
125
|
+
if (tip.externalMemberCount > 0) {
|
|
126
|
+
warnings.push('External Members');
|
|
127
|
+
flag('externalMembers');
|
|
128
|
+
}
|
|
129
|
+
if (tip.totalMemberCount > 0 || tip.externalMemberCount > 0) {
|
|
105
130
|
tipLines.push(
|
|
106
131
|
` *Group members*: ${tip.totalMemberCount} total (${tip.externalMemberCount || 0} external)`
|
|
107
132
|
);
|
|
108
133
|
}
|
|
109
134
|
|
|
135
|
+
// Graph couldn't check this recipient, so its other fields say nothing.
|
|
136
|
+
if (tip.error) {
|
|
137
|
+
warnings.push('Not Checked');
|
|
138
|
+
const code = /^[A-Za-z0-9]{1,64}$/.test(tip.error.code || '')
|
|
139
|
+
? ` (${tip.error.code})`
|
|
140
|
+
: '';
|
|
141
|
+
tipLines.push(
|
|
142
|
+
` **Not checked**: Graph returned an error for this recipient${code}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
110
146
|
// Build section for this recipient
|
|
111
147
|
const statusIcon = warnings.length > 0 ? '⚠' : '✓';
|
|
112
148
|
lines.push(`### ${statusIcon} ${email}`);
|
|
@@ -124,7 +160,29 @@ function formatMailTips(mailTips) {
|
|
|
124
160
|
lines.push('');
|
|
125
161
|
}
|
|
126
162
|
|
|
127
|
-
return { formatted: lines.join('\n'), warningCount };
|
|
163
|
+
return { formatted: lines.join('\n'), warningCount, issues };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Whether Graph returned anything actionable for a recipient: any field a
|
|
168
|
+
* personal Outlook.com account leaves out. A recipientScope of `none` (or
|
|
169
|
+
* none at all) says nothing.
|
|
170
|
+
* @param {object} tip - One mailTips object from Graph
|
|
171
|
+
* @returns {boolean}
|
|
172
|
+
*/
|
|
173
|
+
function hasTipContent(tip) {
|
|
174
|
+
return Boolean(
|
|
175
|
+
tip.error ||
|
|
176
|
+
tip.mailboxFull ||
|
|
177
|
+
tip.deliveryRestricted ||
|
|
178
|
+
tip.isModerated ||
|
|
179
|
+
tip.automaticReplies?.message ||
|
|
180
|
+
tip.maxMessageSize ||
|
|
181
|
+
tip.totalMemberCount ||
|
|
182
|
+
tip.externalMemberCount ||
|
|
183
|
+
tip.customMailTip ||
|
|
184
|
+
(tip.recipientScope && tip.recipientScope !== 'none')
|
|
185
|
+
);
|
|
128
186
|
}
|
|
129
187
|
|
|
130
188
|
/**
|
|
@@ -136,14 +194,7 @@ async function handleGetMailTips(args) {
|
|
|
136
194
|
const { recipients, tipTypes } = args;
|
|
137
195
|
|
|
138
196
|
if (!recipients || recipients.length === 0) {
|
|
139
|
-
return
|
|
140
|
-
content: [
|
|
141
|
-
{
|
|
142
|
-
type: 'text',
|
|
143
|
-
text: 'At least one recipient email address is required.',
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
};
|
|
197
|
+
return toolError('At least one recipient email address is required.');
|
|
147
198
|
}
|
|
148
199
|
|
|
149
200
|
// Normalise recipients to a clean array of email strings
|
|
@@ -171,14 +222,7 @@ async function handleGetMailTips(args) {
|
|
|
171
222
|
recipientList = recipientList.filter((e) => e.length > 0);
|
|
172
223
|
|
|
173
224
|
if (recipientList.length === 0) {
|
|
174
|
-
return
|
|
175
|
-
content: [
|
|
176
|
-
{
|
|
177
|
-
type: 'text',
|
|
178
|
-
text: 'At least one valid recipient email address is required.',
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
};
|
|
225
|
+
return toolError('At least one valid recipient email address is required.');
|
|
182
226
|
}
|
|
183
227
|
|
|
184
228
|
try {
|
|
@@ -206,28 +250,23 @@ async function handleGetMailTips(args) {
|
|
|
206
250
|
text: 'No mail tips returned. Mail Tips is M365-only — personal Outlook.com accounts return empty responses, so recipient validation is unavailable on this account.',
|
|
207
251
|
},
|
|
208
252
|
],
|
|
253
|
+
_meta: {
|
|
254
|
+
recipientCount: 0,
|
|
255
|
+
warningCount: 0,
|
|
256
|
+
allEmpty: true,
|
|
257
|
+
issues: [],
|
|
258
|
+
},
|
|
209
259
|
};
|
|
210
260
|
}
|
|
211
261
|
|
|
212
|
-
const { formatted, warningCount } = formatMailTips(mailTips);
|
|
262
|
+
const { formatted, warningCount, issues } = formatMailTips(mailTips);
|
|
213
263
|
|
|
214
264
|
// F-23: Detect a "fully empty" tips response — every recipient
|
|
215
265
|
// returned with no actionable fields. Personal Outlook.com
|
|
216
266
|
// accounts surface this as a successful empty response rather
|
|
217
267
|
// than a feature-unsupported error, leading to false confidence
|
|
218
268
|
// when callers see "No issues detected".
|
|
219
|
-
const allEmpty = mailTips.every((tip) =>
|
|
220
|
-
const hasContent =
|
|
221
|
-
tip.recipientNotFound ||
|
|
222
|
-
tip.mailboxFull ||
|
|
223
|
-
tip.deliveryRestricted ||
|
|
224
|
-
tip.isModerated ||
|
|
225
|
-
tip.automaticReplies?.message ||
|
|
226
|
-
tip.maxMessageSize ||
|
|
227
|
-
tip.totalMemberCount ||
|
|
228
|
-
tip.customMailTip;
|
|
229
|
-
return !hasContent;
|
|
230
|
-
});
|
|
269
|
+
const allEmpty = mailTips.every((tip) => !hasTipContent(tip));
|
|
231
270
|
|
|
232
271
|
let header = `# Mail Tips\n\n`;
|
|
233
272
|
header += `**Recipients checked**: ${mailTips.length}\n`;
|
|
@@ -244,24 +283,14 @@ async function handleGetMailTips(args) {
|
|
|
244
283
|
recipientCount: mailTips.length,
|
|
245
284
|
warningCount,
|
|
246
285
|
allEmpty,
|
|
286
|
+
issues,
|
|
247
287
|
},
|
|
248
288
|
};
|
|
249
289
|
} catch (error) {
|
|
250
290
|
if (error.message === 'Authentication required') {
|
|
251
|
-
return
|
|
252
|
-
content: [
|
|
253
|
-
{
|
|
254
|
-
type: 'text',
|
|
255
|
-
text: "Authentication required. Please use the 'auth' tool with action=authenticate first.",
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
};
|
|
291
|
+
return authRequiredError();
|
|
259
292
|
}
|
|
260
|
-
return {
|
|
261
|
-
content: [
|
|
262
|
-
{ type: 'text', text: `Error getting mail tips: ${error.message}` },
|
|
263
|
-
],
|
|
264
|
-
};
|
|
293
|
+
return toolError(`Error getting mail tips: ${error.message}`);
|
|
265
294
|
}
|
|
266
295
|
}
|
|
267
296
|
|
package/email/mark-as-read.js
CHANGED
|
@@ -5,6 +5,8 @@ const _config = require('../config'); // Reserved for future use
|
|
|
5
5
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
6
6
|
const { ensureAuthenticated } = require('../auth');
|
|
7
7
|
const { buildMailboxPrefix } = require('../utils/mailbox');
|
|
8
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
9
|
+
const { log } = require('../utils/logger');
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Mark email as read handler
|
|
@@ -17,14 +19,7 @@ async function handleMarkAsRead(args) {
|
|
|
17
19
|
const prefix = buildMailboxPrefix(args.sharedMailbox || args.email || null);
|
|
18
20
|
|
|
19
21
|
if (!emailId) {
|
|
20
|
-
return
|
|
21
|
-
content: [
|
|
22
|
-
{
|
|
23
|
-
type: 'text',
|
|
24
|
-
text: 'Email ID is required.',
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
};
|
|
22
|
+
return toolError('Email ID is required.');
|
|
28
23
|
}
|
|
29
24
|
|
|
30
25
|
try {
|
|
@@ -56,60 +51,29 @@ async function handleMarkAsRead(args) {
|
|
|
56
51
|
],
|
|
57
52
|
};
|
|
58
53
|
} catch (error) {
|
|
59
|
-
|
|
54
|
+
log.debug(
|
|
60
55
|
`Error marking email as ${isRead ? 'read' : 'unread'}: ${error.message}`
|
|
61
56
|
);
|
|
62
57
|
|
|
63
58
|
// Improved error handling with more specific messages
|
|
64
59
|
if (error.message.includes("doesn't belong to the targeted mailbox")) {
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
type: 'text',
|
|
69
|
-
text: `The email ID seems invalid or doesn't belong to your mailbox. Please try with a different email ID.`,
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
};
|
|
60
|
+
return toolError(
|
|
61
|
+
`The email ID seems invalid or doesn't belong to your mailbox. Please try with a different email ID.`
|
|
62
|
+
);
|
|
73
63
|
} else if (error.message.includes('UNAUTHORIZED')) {
|
|
74
|
-
return
|
|
75
|
-
content: [
|
|
76
|
-
{
|
|
77
|
-
type: 'text',
|
|
78
|
-
text: 'Authentication failed. Please re-authenticate and try again.',
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
};
|
|
64
|
+
return authRequiredError();
|
|
82
65
|
} else {
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
type: 'text',
|
|
87
|
-
text: `Failed to mark email as ${isRead ? 'read' : 'unread'}: ${error.message}`,
|
|
88
|
-
},
|
|
89
|
-
],
|
|
90
|
-
};
|
|
66
|
+
return toolError(
|
|
67
|
+
`Failed to mark email as ${isRead ? 'read' : 'unread'}: ${error.message}`
|
|
68
|
+
);
|
|
91
69
|
}
|
|
92
70
|
}
|
|
93
71
|
} catch (error) {
|
|
94
72
|
if (error.message === 'Authentication required') {
|
|
95
|
-
return
|
|
96
|
-
content: [
|
|
97
|
-
{
|
|
98
|
-
type: 'text',
|
|
99
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
};
|
|
73
|
+
return authRequiredError();
|
|
103
74
|
}
|
|
104
75
|
|
|
105
|
-
return {
|
|
106
|
-
content: [
|
|
107
|
-
{
|
|
108
|
-
type: 'text',
|
|
109
|
-
text: `Error accessing email: ${error.message}`,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
};
|
|
76
|
+
return toolError(`Error accessing email: ${error.message}`);
|
|
113
77
|
}
|
|
114
78
|
}
|
|
115
79
|
|
package/email/mime.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
const { callGraphAPIRaw } = require('../utils/graph-api');
|
|
8
8
|
const { ensureAuthenticated } = require('../auth');
|
|
9
9
|
const { buildMailboxPrefix } = require('../utils/mailbox');
|
|
10
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
11
|
+
const { log } = require('../utils/logger');
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Parse MIME headers from raw content
|
|
@@ -98,14 +100,7 @@ async function handleGetMimeContent(args) {
|
|
|
98
100
|
const prefix = buildMailboxPrefix(args.sharedMailbox || args.email || null);
|
|
99
101
|
|
|
100
102
|
if (!emailId) {
|
|
101
|
-
return
|
|
102
|
-
content: [
|
|
103
|
-
{
|
|
104
|
-
type: 'text',
|
|
105
|
-
text: 'Email ID is required.',
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
};
|
|
103
|
+
return toolError('Email ID is required.');
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
try {
|
|
@@ -117,14 +112,9 @@ async function handleGetMimeContent(args) {
|
|
|
117
112
|
const mimeContent = await callGraphAPIRaw(accessToken, emailId, prefix);
|
|
118
113
|
|
|
119
114
|
if (!mimeContent) {
|
|
120
|
-
return
|
|
121
|
-
content
|
|
122
|
-
|
|
123
|
-
type: 'text',
|
|
124
|
-
text: `Failed to retrieve MIME content for email ${emailId}.`,
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
};
|
|
115
|
+
return toolError(
|
|
116
|
+
`Failed to retrieve MIME content for email ${emailId}.`
|
|
117
|
+
);
|
|
128
118
|
}
|
|
129
119
|
|
|
130
120
|
const stats = getMimeStats(mimeContent);
|
|
@@ -174,6 +164,7 @@ async function handleGetMimeContent(args) {
|
|
|
174
164
|
truncated: true,
|
|
175
165
|
maxSizeExceeded: true,
|
|
176
166
|
},
|
|
167
|
+
isError: true,
|
|
177
168
|
};
|
|
178
169
|
}
|
|
179
170
|
|
|
@@ -238,48 +229,22 @@ async function handleGetMimeContent(args) {
|
|
|
238
229
|
},
|
|
239
230
|
};
|
|
240
231
|
} catch (error) {
|
|
241
|
-
|
|
232
|
+
log.debug(`Error getting MIME content: ${error.message}`);
|
|
242
233
|
|
|
243
234
|
if (error.message.includes("doesn't belong to the targeted mailbox")) {
|
|
244
|
-
return
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
type: 'text',
|
|
248
|
-
text: `The email ID seems invalid or doesn't belong to your mailbox.`,
|
|
249
|
-
},
|
|
250
|
-
],
|
|
251
|
-
};
|
|
235
|
+
return toolError(
|
|
236
|
+
`The email ID seems invalid or doesn't belong to your mailbox.`
|
|
237
|
+
);
|
|
252
238
|
}
|
|
253
239
|
|
|
254
|
-
return {
|
|
255
|
-
content: [
|
|
256
|
-
{
|
|
257
|
-
type: 'text',
|
|
258
|
-
text: `Failed to get MIME content: ${error.message}`,
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
};
|
|
240
|
+
return toolError(`Failed to get MIME content: ${error.message}`);
|
|
262
241
|
}
|
|
263
242
|
} catch (error) {
|
|
264
243
|
if (error.message === 'Authentication required') {
|
|
265
|
-
return
|
|
266
|
-
content: [
|
|
267
|
-
{
|
|
268
|
-
type: 'text',
|
|
269
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
270
|
-
},
|
|
271
|
-
],
|
|
272
|
-
};
|
|
244
|
+
return authRequiredError();
|
|
273
245
|
}
|
|
274
246
|
|
|
275
|
-
return {
|
|
276
|
-
content: [
|
|
277
|
-
{
|
|
278
|
-
type: 'text',
|
|
279
|
-
text: `Error accessing email: ${error.message}`,
|
|
280
|
-
},
|
|
281
|
-
],
|
|
282
|
-
};
|
|
247
|
+
return toolError(`Error accessing email: ${error.message}`);
|
|
283
248
|
}
|
|
284
249
|
}
|
|
285
250
|
|
package/email/read.js
CHANGED
|
@@ -9,9 +9,12 @@ const { ensureAuthenticated } = require('../auth');
|
|
|
9
9
|
const {
|
|
10
10
|
formatEmailContent,
|
|
11
11
|
VERBOSITY,
|
|
12
|
+
DEFAULT_LIMITS,
|
|
12
13
|
} = require('../utils/response-formatter');
|
|
13
14
|
const { getEmailFields } = require('../utils/field-presets');
|
|
14
15
|
const { buildMailboxPrefix } = require('../utils/mailbox');
|
|
16
|
+
const { toolError, authRequiredError } = require('../utils/tool-error');
|
|
17
|
+
const { log } = require('../utils/logger');
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
20
|
* Get field preset based on verbosity and options
|
|
@@ -48,17 +51,11 @@ async function handleReadEmail(args) {
|
|
|
48
51
|
const includeHeaders = args.includeHeaders || false;
|
|
49
52
|
// Message IDs are mailbox-scoped: an ID issued by a shared/delegated mailbox
|
|
50
53
|
// is not resolvable under /me. Route to /users/{mailbox} when supplied.
|
|
51
|
-
const
|
|
54
|
+
const sharedMailbox = args.sharedMailbox || args.email || null;
|
|
55
|
+
const prefix = buildMailboxPrefix(sharedMailbox);
|
|
52
56
|
|
|
53
57
|
if (!emailId) {
|
|
54
|
-
return
|
|
55
|
-
content: [
|
|
56
|
-
{
|
|
57
|
-
type: 'text',
|
|
58
|
-
text: 'Email ID is required.',
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
};
|
|
58
|
+
return toolError('Email ID is required.');
|
|
62
59
|
}
|
|
63
60
|
|
|
64
61
|
try {
|
|
@@ -85,20 +82,15 @@ async function handleReadEmail(args) {
|
|
|
85
82
|
);
|
|
86
83
|
|
|
87
84
|
if (!email) {
|
|
88
|
-
return {
|
|
89
|
-
content: [
|
|
90
|
-
{
|
|
91
|
-
type: 'text',
|
|
92
|
-
text: `Email with ID ${emailId} not found.`,
|
|
93
|
-
},
|
|
94
|
-
],
|
|
95
|
-
};
|
|
85
|
+
return toolError(`Email with ID ${emailId} not found.`);
|
|
96
86
|
}
|
|
97
87
|
|
|
98
88
|
// Format using shared formatter (returns Markdown)
|
|
99
89
|
const formattedOutput = formatEmailContent(email, verbosity, {
|
|
100
90
|
includeHeaders: includeHeaders,
|
|
101
91
|
includeAllHeaders: false, // Only important headers by default
|
|
92
|
+
sharedMailbox,
|
|
93
|
+
maxFullBodyChars: DEFAULT_LIMITS.maxFullBodyChars,
|
|
102
94
|
});
|
|
103
95
|
|
|
104
96
|
return {
|
|
@@ -116,49 +108,23 @@ async function handleReadEmail(args) {
|
|
|
116
108
|
},
|
|
117
109
|
};
|
|
118
110
|
} catch (error) {
|
|
119
|
-
|
|
111
|
+
log.debug(`Error reading email: ${error.message}`);
|
|
120
112
|
|
|
121
113
|
// Improved error handling with more specific messages
|
|
122
114
|
if (error.message.includes("doesn't belong to the targeted mailbox")) {
|
|
123
|
-
return
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
type: 'text',
|
|
127
|
-
text: `The email ID seems invalid or doesn't belong to your mailbox. Please try with a different email ID.`,
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
};
|
|
115
|
+
return toolError(
|
|
116
|
+
`The email ID seems invalid or doesn't belong to your mailbox. Please try with a different email ID.`
|
|
117
|
+
);
|
|
131
118
|
} else {
|
|
132
|
-
return {
|
|
133
|
-
content: [
|
|
134
|
-
{
|
|
135
|
-
type: 'text',
|
|
136
|
-
text: `Failed to read email: ${error.message}`,
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
};
|
|
119
|
+
return toolError(`Failed to read email: ${error.message}`);
|
|
140
120
|
}
|
|
141
121
|
}
|
|
142
122
|
} catch (error) {
|
|
143
123
|
if (error.message === 'Authentication required') {
|
|
144
|
-
return
|
|
145
|
-
content: [
|
|
146
|
-
{
|
|
147
|
-
type: 'text',
|
|
148
|
-
text: "Authentication required. Please use the 'authenticate' tool first.",
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
};
|
|
124
|
+
return authRequiredError();
|
|
152
125
|
}
|
|
153
126
|
|
|
154
|
-
return {
|
|
155
|
-
content: [
|
|
156
|
-
{
|
|
157
|
-
type: 'text',
|
|
158
|
-
text: `Error accessing email: ${error.message}`,
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
};
|
|
127
|
+
return toolError(`Error accessing email: ${error.message}`);
|
|
162
128
|
}
|
|
163
129
|
}
|
|
164
130
|
|