proton-mail-bridge-client 2.1.3 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -20
- package/dist/index.js.map +1 -1
- package/dist/services/simple-imap-service.d.ts.map +1 -1
- package/dist/services/simple-imap-service.js +11 -1
- package/dist/services/simple-imap-service.js.map +1 -1
- package/dist/services/smtp-service.d.ts +2 -1
- package/dist/services/smtp-service.d.ts.map +1 -1
- package/dist/services/smtp-service.js +24 -2
- package/dist/services/smtp-service.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export declare function extractUnsubscribeInfo(detail: EmailDetail): {
|
|
|
23
23
|
mailto?: string;
|
|
24
24
|
url?: string;
|
|
25
25
|
};
|
|
26
|
+
export declare function buildReplyHtml(detail: EmailDetail, htmlBody: string): string;
|
|
27
|
+
export declare function buildForwardHtml(detail: EmailDetail, htmlBody?: string): string;
|
|
26
28
|
export declare function getReplyRecipients(detail: EmailDetail, ownerEmail: string, replyAll: boolean): {
|
|
27
29
|
to: string[];
|
|
28
30
|
cc: string[];
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AASA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAA2H,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AAC5N,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AASA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAA2H,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AAC5N,OAAO,EAAmC,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,KAAK,EAMV,mBAAmB,EAEnB,WAAW,EAIX,WAAW,EAIX,gBAAgB,EAIjB,MAAM,kBAAkB,CAAC;AAmB1B,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAmrDnF,eAAO,MAAM,eAAe,aAyB1B,CAAC;AAgGH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAwBZ;AAmED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK3G;AAKD,wBAAgB,0BAA0B,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAI1G;AASD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAa9E;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAS7F;AAuLD,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAW5E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAe/E;AAWD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,OAAO,GAChB;IAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAAC,EAAE,EAAE,MAAM,EAAE,CAAA;CAAE,CAyBhC;AA6ED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CA4CtE;AAcD,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYlE;AAgWD,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAKjI;AA8PD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEzE;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAOzE;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AASD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,EAC9B,MAAM,EAAE,MAAM,EACd,kBAAkB,CAAC,EAAE,MAAM,GAC1B,mBAAmB,EAAE,CA8CvB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,mBAAmB,EAAE,GACtC,mBAAmB,CAmBrB;AAWD,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,MAAM,EAAE,GACjB,GAAG,CAAC,MAAM,GAAG,SAAS,EAAE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAYvE;AAOD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,mBAAmB,CAQ3G;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,mBAAmB,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,mBAAmB,EAAE,CAK/G;AAID,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,EAAE,GAAG,mBAAmB,CASpF;AAsQD,wBAAgB,kBAAkB,IAAI,gBAAgB,CAkGrD;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmrIP;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmE1C"}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, ListResources
|
|
|
12
12
|
import { isMainModule } from "./is-main.js";
|
|
13
13
|
import { AnalyticsService } from "./services/analytics-service.js";
|
|
14
14
|
import { BULK_ITEM_TIMEOUT_MS, describeImapError, isLikelyAuthenticationError, isLikelyConnectionError, isLikelyTlsMismatchError, UID_VALIDITY_MISMATCH_ERROR } from "./services/simple-imap-service.js";
|
|
15
|
-
import { applySignature } from "./services/smtp-service.js";
|
|
15
|
+
import { applySignature, plainTextToHtml } from "./services/smtp-service.js";
|
|
16
16
|
import { ensureValidEmails, foldQuotedHistory, isTextLikeMimeType, isValidEmail, lowerCaseAddress, normalizeBoolean, normalizeLimit, normalizeJsonValue, parseEmailId, parseEmails, projectFields, renderMarkdown, slugifyAccountAddress, splitAccountPrefix, stringifyForJson, withAccountPrefix, } from "./utils/helpers.js";
|
|
17
17
|
import { AccountManager } from "./services/account-manager.js";
|
|
18
18
|
import { logger } from "./utils/logger.js";
|
|
@@ -2069,6 +2069,53 @@ function buildForwardText(detail, body) {
|
|
|
2069
2069
|
.filter((line, index, array) => line !== "" || (index > 0 && array[index - 1] !== ""))
|
|
2070
2070
|
.join("\n");
|
|
2071
2071
|
}
|
|
2072
|
+
// HTML counterparts of buildReplyText/buildForwardText — needed because a
|
|
2073
|
+
// markdown-authored reply/forward (markdownBody) renders its OWN new text to a
|
|
2074
|
+
// real, separate htmlBody, but buildReplyText/buildForwardText only ever wrapped
|
|
2075
|
+
// the PLAIN TEXT `body` with the quoted/forwarded original. That original never
|
|
2076
|
+
// made it into htmlBody at all — an HTML-viewing recipient of a markdown reply
|
|
2077
|
+
// saw only the new text and signature, with the entire original message missing
|
|
2078
|
+
// (confirmed on generated MIME messages), while the text/plain part had it via
|
|
2079
|
+
// buildReplyText. Uses the original message's own detail.html when present
|
|
2080
|
+
// (real markup, not reconstructed); falls back to escaping detail.text/preview
|
|
2081
|
+
// through plainTextToHtml otherwise, same as a plain-text send's own html
|
|
2082
|
+
// alternative (see smtp-service.ts's plainTextToHtml comment).
|
|
2083
|
+
export function buildReplyHtml(detail, htmlBody) {
|
|
2084
|
+
const originalHtml = typeof detail.html === "string" && detail.html
|
|
2085
|
+
? detail.html
|
|
2086
|
+
: plainTextToHtml(detail.text || detail.preview || "");
|
|
2087
|
+
const fromText = formatAddressList(detail.from);
|
|
2088
|
+
const dateText = detail.date || detail.internalDate || "an unknown date";
|
|
2089
|
+
return [
|
|
2090
|
+
htmlBody,
|
|
2091
|
+
`<p>On ${escapeHtmlForQuote(dateText)}, ${escapeHtmlForQuote(fromText || "the sender")} wrote:</p>`,
|
|
2092
|
+
`<blockquote>${originalHtml}</blockquote>`,
|
|
2093
|
+
].join("\n");
|
|
2094
|
+
}
|
|
2095
|
+
export function buildForwardHtml(detail, htmlBody) {
|
|
2096
|
+
const originalHtml = typeof detail.html === "string" && detail.html
|
|
2097
|
+
? detail.html
|
|
2098
|
+
: plainTextToHtml(detail.text || detail.preview || "");
|
|
2099
|
+
return [
|
|
2100
|
+
htmlBody?.trim() || "",
|
|
2101
|
+
"<p>---------- Forwarded message ---------</p>",
|
|
2102
|
+
`<p>From: ${escapeHtmlForQuote(formatAddressList(detail.from))}<br>`,
|
|
2103
|
+
`Date: ${escapeHtmlForQuote(detail.date || detail.internalDate || "")}<br>`,
|
|
2104
|
+
`Subject: ${escapeHtmlForQuote(detail.subject)}<br>`,
|
|
2105
|
+
`To: ${escapeHtmlForQuote(formatAddressList(detail.to))}${detail.cc.length > 0 ? `<br>Cc: ${escapeHtmlForQuote(formatAddressList(detail.cc))}` : ""}</p>`,
|
|
2106
|
+
originalHtml,
|
|
2107
|
+
]
|
|
2108
|
+
.filter((part) => part !== "")
|
|
2109
|
+
.join("\n");
|
|
2110
|
+
}
|
|
2111
|
+
function escapeHtmlForQuote(value) {
|
|
2112
|
+
return value
|
|
2113
|
+
.replace(/&/g, "&")
|
|
2114
|
+
.replace(/</g, "<")
|
|
2115
|
+
.replace(/>/g, ">")
|
|
2116
|
+
.replace(/"/g, """)
|
|
2117
|
+
.replace(/'/g, "'");
|
|
2118
|
+
}
|
|
2072
2119
|
export function getReplyRecipients(detail, ownerEmail, replyAll) {
|
|
2073
2120
|
const owner = lowerCaseAddress(ownerEmail);
|
|
2074
2121
|
const primary = addressValues(detail.replyTo).length > 0 ? detail.replyTo : detail.from;
|
|
@@ -3634,8 +3681,14 @@ export function createServer(config, options = {}) {
|
|
|
3634
3681
|
const includeQuoteReply = normalizeBoolean(args.includeQuote, true);
|
|
3635
3682
|
// Signature goes right after the user's own reply text, before the
|
|
3636
3683
|
// quoted original — not at the very end, after the quote.
|
|
3637
|
-
const signedReply = applySignature(body, htmlBody, normalizeBoolean(args.appendSignature, true));
|
|
3684
|
+
const signedReply = applySignature(body, htmlBody, normalizeBoolean(args.appendSignature, true), isHtml);
|
|
3638
3685
|
const replyBody = includeQuoteReply ? buildReplyText(detail, signedReply.body) : signedReply.body;
|
|
3686
|
+
// A separate htmlBody (the markdown-rendered case) needs the SAME quoted
|
|
3687
|
+
// original merged in — see buildReplyHtml's comment for why this was
|
|
3688
|
+
// previously missing entirely from the html part.
|
|
3689
|
+
const replyHtmlBody = includeQuoteReply && signedReply.htmlBody !== undefined
|
|
3690
|
+
? buildReplyHtml(detail, signedReply.htmlBody)
|
|
3691
|
+
: signedReply.htmlBody;
|
|
3639
3692
|
if (config.runtime.restrictOutboundToSelf) {
|
|
3640
3693
|
const selfAddr = config.smtp.username.toLowerCase();
|
|
3641
3694
|
const allR = [...to, ...cc, ...extraBcc];
|
|
@@ -3654,7 +3707,7 @@ export function createServer(config, options = {}) {
|
|
|
3654
3707
|
subject: prefixedSubject(detail.subject, "Re:"),
|
|
3655
3708
|
body: replyBody,
|
|
3656
3709
|
isHtml,
|
|
3657
|
-
htmlBody:
|
|
3710
|
+
htmlBody: replyHtmlBody,
|
|
3658
3711
|
fromName: fromNameReply,
|
|
3659
3712
|
from: fromReply,
|
|
3660
3713
|
sanitizeHtml: sanitizeHtmlReply,
|
|
@@ -3730,8 +3783,12 @@ export function createServer(config, options = {}) {
|
|
|
3730
3783
|
const includeQuoteRa = normalizeBoolean(args.includeQuote, true);
|
|
3731
3784
|
// Signature goes right after the user's own reply text, before the
|
|
3732
3785
|
// quoted original — not at the very end, after the quote.
|
|
3733
|
-
const signedReplyRa = applySignature(bodyRa, htmlBodyRa, normalizeBoolean(args.appendSignature, true));
|
|
3786
|
+
const signedReplyRa = applySignature(bodyRa, htmlBodyRa, normalizeBoolean(args.appendSignature, true), isHtmlRa);
|
|
3734
3787
|
const replyBodyRa = includeQuoteRa ? buildReplyText(detailRa, signedReplyRa.body) : signedReplyRa.body;
|
|
3788
|
+
// Same fix as reply_to_email — see buildReplyHtml's comment.
|
|
3789
|
+
const replyHtmlBodyRa = includeQuoteRa && signedReplyRa.htmlBody !== undefined
|
|
3790
|
+
? buildReplyHtml(detailRa, signedReplyRa.htmlBody)
|
|
3791
|
+
: signedReplyRa.htmlBody;
|
|
3735
3792
|
const resultRa = await withAudit(auditService, name, args, async () => sendBundleForRa.smtpService.sendEmail({
|
|
3736
3793
|
to: toRa,
|
|
3737
3794
|
cc: ccRa,
|
|
@@ -3739,7 +3796,7 @@ export function createServer(config, options = {}) {
|
|
|
3739
3796
|
subject: prefixedSubject(detailRa.subject, "Re:"),
|
|
3740
3797
|
body: replyBodyRa,
|
|
3741
3798
|
isHtml: isHtmlRa,
|
|
3742
|
-
htmlBody:
|
|
3799
|
+
htmlBody: replyHtmlBodyRa,
|
|
3743
3800
|
fromName: fromNameRa,
|
|
3744
3801
|
from: fromRa,
|
|
3745
3802
|
sanitizeHtml: sanitizeHtmlRa,
|
|
@@ -3838,7 +3895,13 @@ export function createServer(config, options = {}) {
|
|
|
3838
3895
|
// Signature goes right after the user's own note, before the
|
|
3839
3896
|
// "---------- Forwarded message ---------" block — not at the very
|
|
3840
3897
|
// end, after the forwarded content.
|
|
3841
|
-
const signedFwd = applySignature(body ?? "", htmlBody, normalizeBoolean(args.appendSignature, true));
|
|
3898
|
+
const signedFwd = applySignature(body ?? "", htmlBody, normalizeBoolean(args.appendSignature, true), isHtml);
|
|
3899
|
+
// Same fix as reply_to_email/reply_all_email — see buildReplyHtml's
|
|
3900
|
+
// comment (applies equally to a markdown-authored forward's separate
|
|
3901
|
+
// htmlBody, which buildForwardText never touches).
|
|
3902
|
+
const forwardHtmlBody = signedFwd.htmlBody !== undefined
|
|
3903
|
+
? buildForwardHtml(detail, signedFwd.htmlBody)
|
|
3904
|
+
: signedFwd.htmlBody;
|
|
3842
3905
|
const result = await withAudit(auditService, name, args, async () => sendBundleForFwd.smtpService.sendEmail({
|
|
3843
3906
|
to,
|
|
3844
3907
|
cc,
|
|
@@ -3846,7 +3909,7 @@ export function createServer(config, options = {}) {
|
|
|
3846
3909
|
subject: prefixedSubject(detail.subject, "Fwd:"),
|
|
3847
3910
|
body: buildForwardText(detail, signedFwd.body),
|
|
3848
3911
|
isHtml,
|
|
3849
|
-
htmlBody:
|
|
3912
|
+
htmlBody: forwardHtmlBody,
|
|
3850
3913
|
fromName: fromNameFwd,
|
|
3851
3914
|
from: fromFwd,
|
|
3852
3915
|
sanitizeHtml: sanitizeHtmlFwd,
|
|
@@ -3944,14 +4007,19 @@ export function createServer(config, options = {}) {
|
|
|
3944
4007
|
]);
|
|
3945
4008
|
}
|
|
3946
4009
|
case "create_reply_draft": {
|
|
3947
|
-
|
|
4010
|
+
// emailId carries the same "<slug>::" account prefix as everywhere else —
|
|
4011
|
+
// this previously always read via the PRIMARY account's imapService
|
|
4012
|
+
// regardless of that prefix, so replying to a non-primary account's email
|
|
4013
|
+
// either resolved the wrong message or threw not-found.
|
|
4014
|
+
const { bundle: createReplyReadBundle, rest: createReplyEmailIdRest } = resolveAccountForEmailId(requireString(args, "emailId"));
|
|
4015
|
+
const detail = await createReplyReadBundle.imapService.getEmailById(createReplyEmailIdRest);
|
|
3948
4016
|
const body = requireString(args, "body");
|
|
3949
4017
|
const isHtml = normalizeBoolean(args.isHtml, false);
|
|
3950
4018
|
const replyAll = normalizeBoolean(args.replyAll, false);
|
|
3951
4019
|
const attachments = optionalAttachmentList(args.attachments);
|
|
3952
4020
|
const extraCc = parseEmails(optionalString(args, "cc"));
|
|
3953
4021
|
const extraBcc = parseEmails(optionalString(args, "bcc"));
|
|
3954
|
-
const recipients = getReplyRecipients(detail, config.smtp.username, replyAll);
|
|
4022
|
+
const recipients = getReplyRecipients(detail, createReplyReadBundle.config.smtp.username, replyAll);
|
|
3955
4023
|
const cc = uniqueAddresses([...recipients.cc, ...extraCc]);
|
|
3956
4024
|
const to = uniqueAddresses(recipients.to);
|
|
3957
4025
|
ensureValidEmails(to, "to");
|
|
@@ -3960,10 +4028,15 @@ export function createServer(config, options = {}) {
|
|
|
3960
4028
|
if (to.length === 0) {
|
|
3961
4029
|
throw new McpError(ErrorCode.InvalidParams, "Unable to infer reply recipient.");
|
|
3962
4030
|
}
|
|
3963
|
-
// Same from-based draftStore routing as create_draft — see its comment
|
|
4031
|
+
// Same from-based draftStore routing as create_draft — see its comment —
|
|
4032
|
+
// except the no-`from` default is the ACCOUNT THE ORIGINAL MESSAGE BELONGS
|
|
4033
|
+
// TO, not always the primary: a reply drafted against a non-primary
|
|
4034
|
+
// account's email with no explicit `from` should default to replying as
|
|
4035
|
+
// that same account, not silently switch to primary. `from` still wins
|
|
4036
|
+
// when it names a different configured account.
|
|
3964
4037
|
const createReplyDraftBundle = optionalString(args, "from") !== undefined
|
|
3965
|
-
? accountManager.byAddress(optionalString(args, "from")) ??
|
|
3966
|
-
:
|
|
4038
|
+
? accountManager.byAddress(optionalString(args, "from")) ?? createReplyReadBundle
|
|
4039
|
+
: createReplyReadBundle;
|
|
3967
4040
|
const result = await withAudit(auditService, name, args, async () => {
|
|
3968
4041
|
const draft = await createReplyDraftBundle.draftStore.createDraft({
|
|
3969
4042
|
mode: "reply",
|
|
@@ -4009,7 +4082,9 @@ export function createServer(config, options = {}) {
|
|
|
4009
4082
|
]);
|
|
4010
4083
|
}
|
|
4011
4084
|
case "create_forward_draft": {
|
|
4012
|
-
|
|
4085
|
+
// Same emailId account resolution as create_reply_draft — see its comment.
|
|
4086
|
+
const { bundle: createForwardReadBundle, rest: createForwardEmailIdRest } = resolveAccountForEmailId(requireString(args, "emailId"));
|
|
4087
|
+
const detail = await createForwardReadBundle.imapService.getEmailById(createForwardEmailIdRest);
|
|
4013
4088
|
const to = parseEmails(requireString(args, "to"));
|
|
4014
4089
|
const cc = parseEmails(optionalString(args, "cc"));
|
|
4015
4090
|
const bcc = parseEmails(optionalString(args, "bcc"));
|
|
@@ -4027,13 +4102,15 @@ export function createServer(config, options = {}) {
|
|
|
4027
4102
|
const originalAttachments = includeAttachments
|
|
4028
4103
|
? await Promise.all(detail.attachments
|
|
4029
4104
|
.filter((a) => a.id)
|
|
4030
|
-
.map((a) => imapService.getAttachmentForForward(detail.id, a.id)))
|
|
4105
|
+
.map((a) => createForwardReadBundle.imapService.getAttachmentForForward(detail.id, a.id)))
|
|
4031
4106
|
: [];
|
|
4032
4107
|
const attachments = [...originalAttachments, ...callerAttachments];
|
|
4033
|
-
// Same from-based draftStore routing as
|
|
4108
|
+
// Same from-based draftStore routing as create_reply_draft — defaults to
|
|
4109
|
+
// the ORIGINAL MESSAGE's own account, not always primary, when `from`
|
|
4110
|
+
// isn't given.
|
|
4034
4111
|
const createForwardDraftBundle = optionalString(args, "from") !== undefined
|
|
4035
|
-
? accountManager.byAddress(optionalString(args, "from")) ??
|
|
4036
|
-
:
|
|
4112
|
+
? accountManager.byAddress(optionalString(args, "from")) ?? createForwardReadBundle
|
|
4113
|
+
: createForwardReadBundle;
|
|
4037
4114
|
const result = await withAudit(auditService, name, args, async () => {
|
|
4038
4115
|
const draft = await createForwardDraftBundle.draftStore.createDraft({
|
|
4039
4116
|
mode: "forward",
|
|
@@ -6335,10 +6412,15 @@ export function createServer(config, options = {}) {
|
|
|
6335
6412
|
if (to.length === 0) {
|
|
6336
6413
|
throw new McpError(ErrorCode.InvalidParams, "Unable to infer reply recipient.");
|
|
6337
6414
|
}
|
|
6338
|
-
// Same from-based draftStore routing as
|
|
6415
|
+
// Same from-based draftStore routing as create_reply_draft — defaults to
|
|
6416
|
+
// the THREAD's own account (threadReplyBundle, already resolved above from
|
|
6417
|
+
// the threadId's own prefix), not always primary, when `from` isn't given.
|
|
6418
|
+
// Previously always defaulted to primary regardless of which account the
|
|
6419
|
+
// thread actually belonged to — a reply drafted with no explicit `from`
|
|
6420
|
+
// against a non-primary account's thread silently switched accounts.
|
|
6339
6421
|
const createThreadReplyDraftBundle = optionalString(args, "from") !== undefined
|
|
6340
|
-
? accountManager.byAddress(optionalString(args, "from")) ??
|
|
6341
|
-
:
|
|
6422
|
+
? accountManager.byAddress(optionalString(args, "from")) ?? threadReplyBundle
|
|
6423
|
+
: threadReplyBundle;
|
|
6342
6424
|
const result = await withAudit(auditService, name, args, async () => {
|
|
6343
6425
|
const draft = await createThreadReplyDraftBundle.draftStore.createDraft({
|
|
6344
6426
|
mode: "reply",
|