@aaronsb/google-workspace-mcp 4.6.1 → 4.8.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.
Files changed (36) hide show
  1. package/build/accounts/oauth.js +6 -1
  2. package/build/accounts/oauth.js.map +1 -1
  3. package/build/coverage/compare.js +11 -1
  4. package/build/coverage/compare.js.map +1 -1
  5. package/build/factory/manifest/gmail.yaml +257 -6
  6. package/build/factory/safety.js +35 -11
  7. package/build/factory/safety.js.map +1 -1
  8. package/build/server/call-context.d.ts +12 -0
  9. package/build/server/call-context.js +20 -0
  10. package/build/server/call-context.js.map +1 -0
  11. package/build/server/handler.d.ts +4 -0
  12. package/build/server/handler.js +9 -1
  13. package/build/server/handler.js.map +1 -1
  14. package/build/server/preview-gate.d.ts +38 -0
  15. package/build/server/preview-gate.js +107 -0
  16. package/build/server/preview-gate.js.map +1 -0
  17. package/build/server/preview-sets.d.ts +25 -0
  18. package/build/server/preview-sets.js +58 -0
  19. package/build/server/preview-sets.js.map +1 -0
  20. package/build/services/gmail/filters.d.ts +81 -0
  21. package/build/services/gmail/filters.js +457 -0
  22. package/build/services/gmail/filters.js.map +1 -0
  23. package/build/services/gmail/hydrate.d.ts +29 -0
  24. package/build/services/gmail/hydrate.js +116 -0
  25. package/build/services/gmail/hydrate.js.map +1 -0
  26. package/build/services/gmail/labels.d.ts +31 -0
  27. package/build/services/gmail/labels.js +108 -0
  28. package/build/services/gmail/labels.js.map +1 -0
  29. package/build/services/gmail/modify-matching.d.ts +16 -0
  30. package/build/services/gmail/modify-matching.js +149 -0
  31. package/build/services/gmail/modify-matching.js.map +1 -0
  32. package/build/services/gmail/patch.js +76 -116
  33. package/build/services/gmail/patch.js.map +1 -1
  34. package/build/version.d.ts +1 -1
  35. package/build/version.js +1 -1
  36. package/package.json +1 -1
@@ -0,0 +1,457 @@
1
+ /**
2
+ * Gmail filters — routing applied to mail as it arrives. ADR-309, #202.
3
+ *
4
+ * A filter is a standing-effect write: it keeps acting after the call, on messages the
5
+ * call never saw. So the lifecycle is gated. `previewFilter` evaluates a filter against the
6
+ * mail already there and mints a token for that exact filter; `createFilter` and
7
+ * `updateFilter` refuse without it. Every action Gmail supports stays available, and the
8
+ * preview is where a dangerous one (trash, forward) is explained.
9
+ *
10
+ * Gmail has no `filters.update`. `updateFilter` creates the replacement first and deletes
11
+ * the original after, so a failure part-way leaves two filters, never none.
12
+ *
13
+ * Parameters are flat, like every other operation on this tool: criteria are `match*`,
14
+ * actions are `addLabelIds`, `removeLabelIds` and `forwardTo`.
15
+ */
16
+ import { call } from '../../google/client.js';
17
+ import { GoogleApiError } from '../../google/errors.js';
18
+ import { formatShortDate, truncate } from '../../server/formatting/markdown.js';
19
+ import { requireString } from '../../server/handlers/validate.js';
20
+ import { PREVIEW_TTL_MS, canonicalJson, checkPreviewToken, describeTokenFailure, mintPreviewToken, } from '../../server/preview-gate.js';
21
+ import { hydrateMessages } from './hydrate.js';
22
+ import { describeLabelChanges, effectLine, labelIdErrors, labelNames, parseLabelIds } from './labels.js';
23
+ // Named for the operation a token authorizes, so a token offered to its sibling says which it is for.
24
+ const CREATE_KIND = 'manage_email.createFilter';
25
+ const UPDATE_KIND = 'manage_email.updateFilter';
26
+ /** Why a filter write is gated, for the refusal when no token is given. */
27
+ const GATED_BECAUSE = 'It changes what happens to mail that has not arrived yet';
28
+ /** Gmail's per-user limit. */
29
+ const FILTER_LIMIT = 1000;
30
+ /** Gmail stores a filter's size criterion as an int32. */
31
+ const MAX_SIZE = 2 ** 31 - 1;
32
+ /** How many matches the preview walks before it settles for "at least". */
33
+ const COUNT_CAP = 2000;
34
+ const SAMPLE_SIZE = 5;
35
+ /** Tool param → criteria field, for the string criteria. */
36
+ const STRING_CRITERIA = {
37
+ matchFrom: 'from',
38
+ matchTo: 'to',
39
+ matchSubject: 'subject',
40
+ matchQuery: 'query',
41
+ matchNegatedQuery: 'negatedQuery',
42
+ };
43
+ function str(value) {
44
+ if (value === undefined || value === null)
45
+ return undefined;
46
+ const s = String(value).trim();
47
+ return s || undefined;
48
+ }
49
+ function bool(value) {
50
+ return value === true || value === 'true';
51
+ }
52
+ /**
53
+ * A boolean param, strictly. A value read as false drops a criterion and widens the
54
+ * filter, so anything that is not plainly true or false is refused rather than guessed.
55
+ */
56
+ function paramBool(value, name) {
57
+ if (value === undefined || value === null || value === '' || value === false || value === 'false')
58
+ return false;
59
+ if (value === true || value === 'true')
60
+ return true;
61
+ throw new Error(`${name} must be true or false, not '${String(value)}'.`);
62
+ }
63
+ /**
64
+ * Reduce any filter, from our params or from Google, to one canonical spec, so two
65
+ * filters that do the same thing compare equal and a token minted over one matches the
66
+ * other. Empty strings, false booleans and empty label lists are absent.
67
+ */
68
+ export function normalizeSpec(raw) {
69
+ const c = raw.criteria ?? {};
70
+ const a = raw.action ?? {};
71
+ const criteria = {};
72
+ for (const field of ['from', 'to', 'subject', 'query', 'negatedQuery']) {
73
+ const v = str(c[field]);
74
+ if (v)
75
+ criteria[field] = v;
76
+ }
77
+ if (bool(c.hasAttachment))
78
+ criteria.hasAttachment = true;
79
+ if (bool(c.excludeChats))
80
+ criteria.excludeChats = true;
81
+ const size = Number(c.size);
82
+ const comparison = str(c.sizeComparison);
83
+ if (size > 0 && (comparison === 'larger' || comparison === 'smaller')) {
84
+ criteria.size = size;
85
+ criteria.sizeComparison = comparison;
86
+ }
87
+ const action = {};
88
+ const add = parseLabelIds(a.addLabelIds);
89
+ const remove = parseLabelIds(a.removeLabelIds);
90
+ const forward = str(a.forward)?.toLowerCase();
91
+ if (add)
92
+ action.addLabelIds = add;
93
+ if (remove)
94
+ action.removeLabelIds = remove;
95
+ if (forward)
96
+ action.forward = forward;
97
+ return { criteria, action };
98
+ }
99
+ /** Build and validate the filter a call describes. Throws a sentence naming what is missing. */
100
+ export function buildFilterSpec(params) {
101
+ const criteria = {};
102
+ for (const [param, field] of Object.entries(STRING_CRITERIA))
103
+ criteria[field] = params[param];
104
+ criteria.hasAttachment = paramBool(params.matchHasAttachment, 'matchHasAttachment');
105
+ criteria.excludeChats = paramBool(params.matchExcludeChats, 'matchExcludeChats');
106
+ const size = params.matchSize;
107
+ const comparison = str(params.matchSizeComparison);
108
+ if (comparison && comparison !== 'larger' && comparison !== 'smaller') {
109
+ throw new Error(`matchSizeComparison must be 'larger' or 'smaller', not '${comparison}'.`);
110
+ }
111
+ const hasSize = size !== undefined && size !== null && size !== '';
112
+ if (hasSize !== Boolean(comparison)) {
113
+ throw new Error('matchSize and matchSizeComparison go together: a size in bytes, and whether matches are larger or smaller.');
114
+ }
115
+ const bytes = Number(size);
116
+ if (hasSize && !(Number.isInteger(bytes) && bytes > 0 && bytes <= MAX_SIZE)) {
117
+ throw new Error(`matchSize must be a whole number of bytes from 1 to ${MAX_SIZE}, not '${String(size)}'.`);
118
+ }
119
+ criteria.size = hasSize ? bytes : undefined;
120
+ criteria.sizeComparison = comparison;
121
+ const spec = normalizeSpec({
122
+ criteria,
123
+ action: { addLabelIds: params.addLabelIds, removeLabelIds: params.removeLabelIds, forward: params.forwardTo },
124
+ });
125
+ const { excludeChats: _ignored, ...matching } = spec.criteria;
126
+ if (Object.keys(matching).length === 0) {
127
+ throw new Error('A filter needs at least one criterion: matchFrom, matchTo, matchSubject, matchQuery, ' +
128
+ 'matchNegatedQuery, matchHasAttachment, or matchSize with matchSizeComparison.');
129
+ }
130
+ if (Object.keys(spec.action).length === 0) {
131
+ throw new Error('A filter needs at least one action: addLabelIds, removeLabelIds (e.g. INBOX to skip the inbox), or forwardTo.');
132
+ }
133
+ return spec;
134
+ }
135
+ /** The Gmail search a filter's criteria amount to. Filters and search share one syntax. */
136
+ export function criteriaToQuery(c) {
137
+ const parts = [];
138
+ if (c.from)
139
+ parts.push(`from:(${c.from})`);
140
+ if (c.to)
141
+ parts.push(`to:(${c.to})`);
142
+ if (c.subject)
143
+ parts.push(`subject:(${c.subject})`);
144
+ if (c.query)
145
+ parts.push(`(${c.query})`);
146
+ if (c.negatedQuery)
147
+ parts.push(`-(${c.negatedQuery})`);
148
+ if (c.hasAttachment)
149
+ parts.push('has:attachment');
150
+ if (c.size && c.sizeComparison)
151
+ parts.push(`${c.sizeComparison}:${c.size}`);
152
+ if (c.excludeChats)
153
+ parts.push('-in:chats');
154
+ return parts.join(' ');
155
+ }
156
+ /** The tool params that describe this filter, for recreating or editing it. */
157
+ export function specToParams(spec) {
158
+ const out = {};
159
+ for (const [param, field] of Object.entries(STRING_CRITERIA)) {
160
+ const v = spec.criteria[field];
161
+ if (v)
162
+ out[param] = v;
163
+ }
164
+ if (spec.criteria.hasAttachment)
165
+ out.matchHasAttachment = true;
166
+ if (spec.criteria.excludeChats)
167
+ out.matchExcludeChats = true;
168
+ if (spec.criteria.size) {
169
+ out.matchSize = spec.criteria.size;
170
+ out.matchSizeComparison = spec.criteria.sizeComparison;
171
+ }
172
+ if (spec.action.addLabelIds)
173
+ out.addLabelIds = spec.action.addLabelIds.join(',');
174
+ if (spec.action.removeLabelIds)
175
+ out.removeLabelIds = spec.action.removeLabelIds.join(',');
176
+ if (spec.action.forward)
177
+ out.forwardTo = spec.action.forward;
178
+ return out;
179
+ }
180
+ // --- What a filter does, in words ---
181
+ export function describeActions(action, names) {
182
+ const effects = describeLabelChanges(action, names, 'arrival');
183
+ if (action.forward) {
184
+ effects.push({
185
+ text: `forwarded in full to ${action.forward}`,
186
+ detail: 'every time, with no further prompt; whoever reads that address reads every match',
187
+ warn: true,
188
+ });
189
+ }
190
+ return effects;
191
+ }
192
+ function effectLines(action, labelNames) {
193
+ return describeActions(action, labelNames).map(effectLine);
194
+ }
195
+ function summarize(spec, labelNames) {
196
+ return describeActions(spec.action, labelNames).map((e) => e.text).join('; ');
197
+ }
198
+ function describeFilter(spec, labelNames) {
199
+ return [`**Criteria:** \`${criteriaToQuery(spec.criteria)}\``, '**Actions:** matching mail is', ...effectLines(spec.action, labelNames)];
200
+ }
201
+ // --- Reading what the account has ---
202
+ async function listFilters(account) {
203
+ const raw = await call('gmail', 'users.settings.filters.list', { userId: 'me' }, { account });
204
+ const filters = (raw?.filter ?? []);
205
+ return filters.map((f) => ({ id: String(f.id ?? ''), ...normalizeSpec(f) }));
206
+ }
207
+ async function getStoredFilter(account, id) {
208
+ try {
209
+ const f = await call('gmail', 'users.settings.filters.get', { userId: 'me', id }, { account });
210
+ return { id: String(f.id ?? id), ...normalizeSpec(f) };
211
+ }
212
+ catch (err) {
213
+ if (err instanceof GoogleApiError && err.status === 404)
214
+ return undefined;
215
+ throw err;
216
+ }
217
+ }
218
+ async function forwardingAddresses(account) {
219
+ const raw = await call('gmail', 'users.settings.forwardingAddresses.list', { userId: 'me' }, { account });
220
+ return (raw?.forwardingAddresses ?? []);
221
+ }
222
+ const same = (a, b) => canonicalJson(a) === canonicalJson(b);
223
+ const sameCriteria = (a, b) => canonicalJson(a.criteria) === canonicalJson(b.criteria);
224
+ const asSpec = (f) => ({ criteria: f.criteria, action: f.action });
225
+ /** Walk ids-only pages of the filter's query, up to COUNT_CAP. */
226
+ async function countMatches(account, q) {
227
+ let count = 0;
228
+ let estimate;
229
+ let pageToken;
230
+ const sampleIds = [];
231
+ do {
232
+ const page = await call('gmail', 'users.messages.list', {
233
+ userId: 'me', q, maxResults: 500, ...(pageToken ? { pageToken } : {}),
234
+ }, { account });
235
+ const ids = (page?.messages ?? []).map((m) => m.id);
236
+ if (estimate === undefined && typeof page?.resultSizeEstimate === 'number')
237
+ estimate = page.resultSizeEstimate;
238
+ count += ids.length;
239
+ sampleIds.push(...ids.slice(0, SAMPLE_SIZE - sampleIds.length));
240
+ pageToken = typeof page?.nextPageToken === 'string' && page.nextPageToken ? page.nextPageToken : undefined;
241
+ } while (pageToken && count < COUNT_CAP);
242
+ return { count, exact: !pageToken, estimate, sampleIds };
243
+ }
244
+ function refused(text, refs = {}) {
245
+ return { text, refs: { ...refs, blocked: true } };
246
+ }
247
+ // --- Operations ---
248
+ /** `filters` — every filter on the account, one line each. */
249
+ export async function handleListFilters(_params, account) {
250
+ const [filters, names] = await Promise.all([listFilters(account), labelNames(account)]);
251
+ if (filters.length === 0)
252
+ return { text: 'No filters on this account.', refs: { count: 0, filterIds: [] } };
253
+ const rows = filters.map((f) => `${f.id} | ${truncate(criteriaToQuery(f.criteria), 70)} | ${summarize(f, names)}`);
254
+ return {
255
+ text: `## Filters (${filters.length} of ${FILTER_LIMIT.toLocaleString('en-US')})\n\n${rows.join('\n')}`,
256
+ refs: { count: filters.length, filterId: filters[0].id, filterIds: filters.map((f) => f.id) },
257
+ };
258
+ }
259
+ /** `getFilter` — one filter, in words, with the parameters that recreate or edit it. */
260
+ export async function handleGetFilter(params, account) {
261
+ const id = requireString(params, 'filterId');
262
+ const [filter, names] = await Promise.all([getStoredFilter(account, id), labelNames(account)]);
263
+ if (!filter)
264
+ return refused(`No filter ${id} on ${account}. List them with \`filters\`.`, { filterId: id });
265
+ const recreate = specToParams(filter);
266
+ return {
267
+ text: [
268
+ `## Filter ${id}`, '',
269
+ ...describeFilter(filter, names), '',
270
+ `**As parameters** (for previewFilter, with filterId to edit it): \`${JSON.stringify(recreate)}\``,
271
+ ].join('\n'),
272
+ refs: { filterId: id, query: criteriaToQuery(filter.criteria), params: recreate },
273
+ };
274
+ }
275
+ /**
276
+ * `previewFilter` — evaluate a filter without creating it, and mint the token the write
277
+ * needs. With `filterId`, evaluates replacing that filter and mints an update token.
278
+ *
279
+ * Problems that would make the write fail or do nothing (an unknown label, an unverified
280
+ * forwarding address, an exact duplicate) are errors and withhold the token. Consequences
281
+ * worth a second look are shown, not refused.
282
+ */
283
+ export async function handlePreviewFilter(params, account) {
284
+ const spec = buildFilterSpec(params);
285
+ const replacingId = str(params.filterId);
286
+ const [filters, names, forwarding] = await Promise.all([
287
+ listFilters(account),
288
+ labelNames(account),
289
+ spec.action.forward ? forwardingAddresses(account) : Promise.resolve([]),
290
+ ]);
291
+ const errors = [];
292
+ const notes = [];
293
+ const replacing = replacingId ? filters.find((f) => f.id === replacingId) : undefined;
294
+ if (replacingId && !replacing)
295
+ errors.push(`No filter ${replacingId} exists on this account. List them with \`filters\`.`);
296
+ if (replacing && same(asSpec(replacing), spec))
297
+ errors.push(`Filter ${replacingId} already has exactly these criteria and actions; there is nothing to change.`);
298
+ errors.push(...labelIdErrors(spec.action, names));
299
+ if (spec.action.forward) {
300
+ const address = forwarding.find((a) => a.forwardingEmail.toLowerCase() === spec.action.forward);
301
+ if (!address) {
302
+ errors.push(`${spec.action.forward} is not a forwarding address on this account. Gmail forwards only to ` +
303
+ 'addresses added and verified in Gmail settings, under Forwarding and POP/IMAP.');
304
+ }
305
+ else if (address.verificationStatus !== 'accepted') {
306
+ errors.push(`${spec.action.forward} is a forwarding address but is not verified yet (${address.verificationStatus ?? 'unknown'}).`);
307
+ }
308
+ }
309
+ const others = filters.filter((f) => f.id !== replacingId);
310
+ const duplicate = others.find((f) => same(asSpec(f), spec));
311
+ if (duplicate)
312
+ errors.push(`Filter ${duplicate.id} already does exactly this.`);
313
+ for (const f of others) {
314
+ if (f !== duplicate && sameCriteria(f, spec)) {
315
+ notes.push(`⚠ Filter ${f.id} has the same criteria and does: ${summarize(f, names)}. Both will apply to every match.`);
316
+ }
317
+ }
318
+ if (filters.length >= FILTER_LIMIT) {
319
+ errors.push(replacing
320
+ ? `This account has ${filters.length} filters, Gmail's limit. An update creates the replacement before deleting ` +
321
+ `the original, and Gmail refuses the 1,001st. Delete ${replacingId} first (deleteFilter returns its parameters), ` +
322
+ 'then preview and create the replacement.'
323
+ : `This account has ${filters.length} filters, Gmail's limit.`);
324
+ }
325
+ else if (filters.length >= FILTER_LIMIT - 50)
326
+ notes.push(`This account has ${filters.length} of Gmail's ${FILTER_LIMIT} filters.`);
327
+ const query = criteriaToQuery(spec.criteria);
328
+ const matches = await countMatches(account, query);
329
+ const sample = matches.sampleIds.length ? await hydrateMessages(matches.sampleIds.map((id) => ({ id })), account) : [];
330
+ const write = replacing ? 'updateFilter' : 'createFilter';
331
+ const lines = [replacing ? `## Preview: replace filter ${replacingId}` : '## Preview: new filter', ''];
332
+ lines.push(...describeFilter(spec, names), '');
333
+ if (replacing) {
334
+ lines.push(`**Replaces** filter ${replacingId}, which is:`, `- criteria \`${criteriaToQuery(replacing.criteria)}\``);
335
+ lines.push(...describeActions(replacing.action, names).map((e) => `- ${e.text}`));
336
+ lines.push(`Gmail has no in-place update: the replacement gets a new id, and ${replacingId} is deleted after it is created.`, '');
337
+ }
338
+ if (matches.exact) {
339
+ lines.push(matches.count === 0
340
+ ? '**Matches today:** none. A filter that matches nothing yet may be mistyped, or may be for mail that has not started arriving.'
341
+ : `**Matches today:** ${matches.count} message${matches.count === 1 ? '' : 's'}.`);
342
+ }
343
+ else {
344
+ const est = matches.estimate !== undefined ? ` Google estimates about ${matches.estimate}, which is an estimate.` : '';
345
+ lines.push(`**Matches today:** at least ${matches.count}; counting stopped there.${est}`);
346
+ }
347
+ if (sample.length) {
348
+ lines.push('', 'Most recent matches:');
349
+ for (const m of sample) {
350
+ lines.push(m.error
351
+ ? `${String(m.id)} | ⚠ ${String(m.error)}`
352
+ : `${String(m.id)} | ${truncate(String(m.from ?? ''), 30)} | ${truncate(String(m.subject ?? '(no subject)'), 50)} | ${formatShortDate(m.date)}`);
353
+ }
354
+ }
355
+ if (matches.count > 0) {
356
+ const relabel = spec.action.addLabelIds || spec.action.removeLabelIds;
357
+ lines.push('', 'A filter acts only on mail that arrives after it exists; the messages counted above stay as they are (spam and trash were not counted).' +
358
+ (relabel
359
+ ? ` To give them the same label changes, call \`previewModifyMatching\` with query \`${query}\` and the same addLabelIds and removeLabelIds, then \`modifyMatching\` with its token.`
360
+ : ''));
361
+ }
362
+ if (notes.length)
363
+ lines.push('', ...notes);
364
+ const refs = {
365
+ query,
366
+ matchCount: matches.count,
367
+ matchCountExact: matches.exact,
368
+ ...(matches.estimate !== undefined ? { resultSizeEstimate: matches.estimate } : {}),
369
+ ...(replacingId ? { filterId: replacingId } : {}),
370
+ };
371
+ if (errors.length) {
372
+ lines.push('', `### Cannot ${replacing ? 'replace' : 'create'} this filter`, ...errors.map((e) => `- ${e}`), '', 'No previewToken issued. Fix the parameters and preview again.');
373
+ return { text: lines.join('\n'), refs: { ...refs, errors } };
374
+ }
375
+ const token = replacing
376
+ ? mintPreviewToken(UPDATE_KIND, account, { filterId: replacingId, before: asSpec(replacing), spec })
377
+ : mintPreviewToken(CREATE_KIND, account, { spec });
378
+ lines.push('', replacing
379
+ ? `To apply this change, call \`${write}\` with filterId ${replacingId}, the same parameters, and previewToken:`
380
+ : `To create it, call \`${write}\` with the same parameters and previewToken:`, `\`${token}\``, `Good for ${PREVIEW_TTL_MS / 60_000} minutes, for this account and exactly these parameters.`);
381
+ return { text: lines.join('\n'), refs: { ...refs, previewToken: token } };
382
+ }
383
+ /** `createFilter` — create a filter its preview has already shown. */
384
+ export async function handleCreateFilter(params, account) {
385
+ const spec = buildFilterSpec(params);
386
+ const check = checkPreviewToken(params.previewToken, CREATE_KIND, account, { spec });
387
+ if (!check.ok)
388
+ return refused(`**Not created.** ${describeTokenFailure(check, 'createFilter', 'previewFilter', GATED_BECAUSE)}`);
389
+ const [filters, names] = await Promise.all([listFilters(account), labelNames(account)]);
390
+ const duplicate = filters.find((f) => same(asSpec(f), spec));
391
+ if (duplicate)
392
+ return refused(`**Not created.** Filter ${duplicate.id} already does exactly this.`, { filterId: duplicate.id });
393
+ const created = await call('gmail', 'users.settings.filters.create', { userId: 'me', ...spec }, { account });
394
+ const id = String(created.id ?? '');
395
+ return {
396
+ text: [`Created filter **${id}**.`, '', ...describeFilter(spec, names), '',
397
+ 'It acts on mail arriving from now on; messages already in the mailbox are unchanged.'].join('\n'),
398
+ refs: { filterId: id, created: true },
399
+ };
400
+ }
401
+ /**
402
+ * `updateFilter` — replace a filter with the one its preview showed.
403
+ *
404
+ * The token covers the filter being replaced as it was at preview time, so a filter that
405
+ * changed in the meantime is not replaced on the strength of a preview of something else.
406
+ */
407
+ export async function handleUpdateFilter(params, account) {
408
+ const filterId = requireString(params, 'filterId');
409
+ const spec = buildFilterSpec(params);
410
+ const [current, filters, names] = await Promise.all([getStoredFilter(account, filterId), listFilters(account), labelNames(account)]);
411
+ if (!current) {
412
+ return refused(`**Not changed.** Filter ${filterId} no longer exists on ${account}. To make it fresh, preview without filterId and use createFilter.`, { filterId });
413
+ }
414
+ const check = checkPreviewToken(params.previewToken, UPDATE_KIND, account, { filterId, before: asSpec(current), spec });
415
+ if (!check.ok)
416
+ return refused(`**Not changed.** ${describeTokenFailure(check, 'updateFilter', 'previewFilter (with filterId)', GATED_BECAUSE)}`, { filterId });
417
+ const duplicate = filters.find((f) => f.id !== filterId && same(asSpec(f), spec));
418
+ if (duplicate)
419
+ return refused(`**Not changed.** Filter ${duplicate.id} already does exactly this.`, { filterId });
420
+ const created = await call('gmail', 'users.settings.filters.create', { userId: 'me', ...spec }, { account });
421
+ const newId = String(created.id ?? '');
422
+ try {
423
+ await call('gmail', 'users.settings.filters.delete', { userId: 'me', id: filterId }, { account });
424
+ }
425
+ catch (err) {
426
+ // Already gone is the outcome the delete was for.
427
+ if (!(err instanceof GoogleApiError && err.status === 404)) {
428
+ // Half done is a failure, not a success with a caveat: thrown, so a queue with
429
+ // onError 'bail' stops here instead of running on.
430
+ const why = err instanceof Error ? err.message : String(err);
431
+ throw new Error(`updateFilter half done: created the replacement filter ${newId}, but could not delete the original ` +
432
+ `${filterId} (${why}). Both are active now. Remove the original with deleteFilter filterId ${filterId}.`);
433
+ }
434
+ }
435
+ return {
436
+ text: [`Replaced filter **${filterId}** with **${newId}**. Gmail has no in-place update, so the filter has a new id.`, '',
437
+ ...describeFilter(spec, names), '',
438
+ 'It acts on mail arriving from now on; messages already in the mailbox are unchanged.'].join('\n'),
439
+ refs: { filterId: newId, replacedFilterId: filterId, originalDeleted: true },
440
+ };
441
+ }
442
+ /** `deleteFilter` — stop a filter, and return what it was so it can be put back. */
443
+ export async function handleDeleteFilter(params, account) {
444
+ const filterId = requireString(params, 'filterId');
445
+ const [filter, names] = await Promise.all([getStoredFilter(account, filterId), labelNames(account)]);
446
+ if (!filter)
447
+ return refused(`No filter ${filterId} on ${account}; nothing deleted. List them with \`filters\`.`, { filterId });
448
+ await call('gmail', 'users.settings.filters.delete', { userId: 'me', id: filterId }, { account });
449
+ const recreate = specToParams(filter);
450
+ return {
451
+ text: [`Deleted filter **${filterId}**. Mail it matched now arrives without its routing.`, '', 'It was:',
452
+ ...describeFilter(filter, names), '',
453
+ `To put it back, preview and create with: \`${JSON.stringify(recreate)}\``].join('\n'),
454
+ refs: { filterId, deleted: true, params: recreate },
455
+ };
456
+ }
457
+ //# sourceMappingURL=filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filters.js","sourceRoot":"","sources":["../../../src/services/gmail/filters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Hydration — turn the bare ids Gmail's list methods return into rows a reader can use.
3
+ *
4
+ * `messages.list` and `drafts.list` return ids and nothing else, so every row costs one
5
+ * `messages.get`. Shared by search, triage, listDrafts and the filter preview.
6
+ */
7
+ /**
8
+ * Hydrate message IDs with metadata (From, Subject, Date, snippet).
9
+ * Reused from the original email handler.
10
+ */
11
+ export declare function hydrateMessages(messageIds: Array<{
12
+ id: string;
13
+ }>, account: string): Promise<Record<string, unknown>[]>;
14
+ /**
15
+ * Hydrate draft resources with message metadata (From, To, Subject, Date, snippet).
16
+ *
17
+ * users.drafts.list returns one bare {id: draftId, message: {id, threadId}} per draft —
18
+ * enough to identify a draft, nothing to tell drafts apart. Each draft's message is
19
+ * fetched (metadata format, exactly like the search/triage hydration) and the DRAFT id
20
+ * stays on the row, because it — not the message id — is what deleteDraft addresses.
21
+ */
22
+ export declare function hydrateDrafts(drafts: Array<{
23
+ id: string;
24
+ message?: {
25
+ id?: string;
26
+ threadId?: string;
27
+ };
28
+ }>, account: string): Promise<Record<string, unknown>[]>;
29
+ export declare function mapLimited<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]>;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Hydration — turn the bare ids Gmail's list methods return into rows a reader can use.
3
+ *
4
+ * `messages.list` and `drafts.list` return ids and nothing else, so every row costs one
5
+ * `messages.get`. Shared by search, triage, listDrafts and the filter preview.
6
+ */
7
+ import { call } from '../../google/client.js';
8
+ import { GoogleApiError } from '../../google/errors.js';
9
+ /**
10
+ * Hydrate message IDs with metadata (From, Subject, Date, snippet).
11
+ * Reused from the original email handler.
12
+ */
13
+ export async function hydrateMessages(messageIds, account) {
14
+ // Google throttles per USER, not per process, so this burst competes with every
15
+ // other client signed in to the same account. Firing all 50 at once is the surest
16
+ // way to be told no. A modest window is barely slower — the calls still overlap —
17
+ // and it is far less likely to be throttled in the first place.
18
+ return mapLimited(messageIds, HYDRATE_CONCURRENCY, async (msg) => {
19
+ try {
20
+ const data = await call('gmail', 'users.messages.get', {
21
+ userId: 'me',
22
+ id: msg.id,
23
+ format: 'metadata',
24
+ metadataHeaders: ['From', 'Subject', 'Date'],
25
+ }, { account });
26
+ const headers = (data.payload?.headers ?? []);
27
+ const getHeader = (name) => headers.find(h => h.name.toLowerCase() === name.toLowerCase())?.value;
28
+ return {
29
+ id: data.id,
30
+ threadId: data.threadId,
31
+ from: getHeader('from'),
32
+ subject: getHeader('subject'),
33
+ date: getHeader('date'),
34
+ snippet: data.snippet,
35
+ };
36
+ }
37
+ catch (err) {
38
+ // A message we could not READ must never look like a message with NOTHING IN IT.
39
+ //
40
+ // This used to `catch { return { id: msg.id } }` — every failure became a row
41
+ // with no sender, no subject and no date, indistinguishable from a genuinely
42
+ // empty message. Under rate limiting the tail of an inbox silently rendered as
43
+ // blank lines, and the tool reported success while showing the user nothing.
44
+ //
45
+ // The client retries a 429 now, so reaching here means it did not recover. Say
46
+ // so, in the row, where the person reading the list will actually see it.
47
+ return {
48
+ id: msg.id,
49
+ error: err instanceof GoogleApiError
50
+ ? `could not load (${err.status}${err.reason ? ' ' + err.reason : ''})`
51
+ : `could not load (${err instanceof Error ? err.message : String(err)})`,
52
+ };
53
+ }
54
+ });
55
+ }
56
+ /** Concurrent, but bounded. */
57
+ const HYDRATE_CONCURRENCY = 8;
58
+ /**
59
+ * Hydrate draft resources with message metadata (From, To, Subject, Date, snippet).
60
+ *
61
+ * users.drafts.list returns one bare {id: draftId, message: {id, threadId}} per draft —
62
+ * enough to identify a draft, nothing to tell drafts apart. Each draft's message is
63
+ * fetched (metadata format, exactly like the search/triage hydration) and the DRAFT id
64
+ * stays on the row, because it — not the message id — is what deleteDraft addresses.
65
+ */
66
+ export async function hydrateDrafts(drafts, account) {
67
+ return mapLimited(drafts, HYDRATE_CONCURRENCY, async (draft) => {
68
+ const draftId = draft.id;
69
+ const messageId = draft.message?.id;
70
+ if (!messageId) {
71
+ return { draftId, error: 'draft carries no message id' };
72
+ }
73
+ try {
74
+ const data = await call('gmail', 'users.messages.get', {
75
+ userId: 'me',
76
+ id: messageId,
77
+ format: 'metadata',
78
+ metadataHeaders: ['From', 'To', 'Subject', 'Date'],
79
+ }, { account });
80
+ const headers = (data.payload?.headers ?? []);
81
+ const getHeader = (name) => headers.find(h => h.name.toLowerCase() === name.toLowerCase())?.value;
82
+ return {
83
+ draftId,
84
+ messageId: data.id,
85
+ threadId: data.threadId,
86
+ to: getHeader('to'),
87
+ from: getHeader('from'),
88
+ subject: getHeader('subject'),
89
+ date: getHeader('date'),
90
+ snippet: data.snippet,
91
+ };
92
+ }
93
+ catch (err) {
94
+ return {
95
+ draftId,
96
+ messageId,
97
+ error: err instanceof GoogleApiError
98
+ ? `could not load (${err.status}${err.reason ? ' ' + err.reason : ''})`
99
+ : `could not load (${err instanceof Error ? err.message : String(err)})`,
100
+ };
101
+ }
102
+ });
103
+ }
104
+ export async function mapLimited(items, limit, fn) {
105
+ const results = new Array(items.length);
106
+ let next = 0;
107
+ const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
108
+ while (next < items.length) {
109
+ const i = next++;
110
+ results[i] = await fn(items[i]); // fn never rejects — it catches internally
111
+ }
112
+ });
113
+ await Promise.all(workers);
114
+ return results;
115
+ }
116
+ //# sourceMappingURL=hydrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydrate.js","sourceRoot":"","sources":["../../../src/services/gmail/hydrate.ts"],"names":[],"mappings":"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"}