@pome-sh/cli 0.21.5 → 0.21.7

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.
@@ -1,686 +1,12 @@
1
- import { statePath, defineCheck, VACUITY_SENTINEL, childStatePath, VACUITY_SENTINEL_NUMBER } from './chunk-NY55QTVQ.js';
1
+ export { GMAIL_CHECKS } from './chunk-5ETJZJR6.js';
2
+ import { gmailErrorEnvelope, gmailSeedSchema, parseSeed, defaultSeedState, notFound, invalidArgument, checkFault, unsupported, validateSearchQuery, SEARCH_MAILBOX_MESSAGE_BUDGET, GmailError, parseSearchQuery, CATEGORY_LABELS, parseDate, parseSize, KNOWN_FIELDS, parseDuration, assertHasOperator } from './chunk-NJ246QPJ.js';
3
+ export { DEFAULT_GMAIL_AGENT_EMAIL, GmailError, SEARCH_MAILBOX_MESSAGE_BUDGET, agentPathInboxMailbox, defaultSeedState, gmailErrorEnvelope, gmailSeedSchema, loadSeedFromEnv, parseSearchQuery, parseSeed, validateSearchQuery } from './chunk-NJ246QPJ.js';
4
+ import './chunk-NY55QTVQ.js';
2
5
  import { defineTwin, openTwinDatabase, createApp } from './chunk-LZIPGCQJ.js';
3
- import { z, ZodError } from 'zod';
6
+ import './chunk-VBATFCWR.js';
7
+ import './chunk-SG6ZTIMT.js';
4
8
  import { createHash, createHmac, timingSafeEqual, randomBytes } from 'node:crypto';
5
-
6
- var GmailError = class extends Error {
7
- status;
8
- reason;
9
- constructor(status, reason, message2) {
10
- super(message2);
11
- this.status = status;
12
- this.reason = reason;
13
- this.name = "GmailError";
14
- }
15
- };
16
- function gmailErrorEnvelope(error) {
17
- if (error instanceof GmailError) {
18
- return {
19
- status: error.status,
20
- body: {
21
- error: {
22
- code: error.status,
23
- message: error.message,
24
- errors: [{ message: error.message, domain: "global", reason: error.reason }],
25
- status: googleStatus(error.status)
26
- }
27
- }
28
- };
29
- }
30
- if (error instanceof ZodError || error instanceof Error && error.name === "ZodError") {
31
- return {
32
- status: 400,
33
- body: {
34
- error: {
35
- code: 400,
36
- message: "Invalid request",
37
- errors: [{ message: "Invalid request", domain: "global", reason: "invalidArgument" }],
38
- status: "INVALID_ARGUMENT"
39
- }
40
- }
41
- };
42
- }
43
- if (error instanceof SyntaxError) {
44
- return {
45
- status: 400,
46
- body: {
47
- error: {
48
- code: 400,
49
- message: "Invalid JSON",
50
- errors: [{ message: "Invalid JSON", domain: "global", reason: "invalidArgument" }],
51
- status: "INVALID_ARGUMENT"
52
- }
53
- }
54
- };
55
- }
56
- if (error instanceof Error && (error.name === "TwinError" || error.name === "UnknownToolError")) {
57
- const unknownTool = error.name === "UnknownToolError";
58
- const status = unknownTool ? 404 : typeof error.status === "number" ? error.status : 400;
59
- return {
60
- status,
61
- body: {
62
- error: {
63
- code: status,
64
- message: error.message,
65
- errors: [
66
- {
67
- message: error.message,
68
- domain: "global",
69
- reason: unknownTool ? "notFound" : "invalidArgument"
70
- }
71
- ],
72
- status: googleStatus(status)
73
- }
74
- }
75
- };
76
- }
77
- return {
78
- status: 500,
79
- body: {
80
- error: {
81
- code: 500,
82
- message: "Internal error",
83
- errors: [{ message: "Internal error", domain: "global", reason: "backendError" }],
84
- status: "INTERNAL"
85
- }
86
- }
87
- };
88
- }
89
- function notFound(resource = "Requested entity") {
90
- throw new GmailError(404, "notFound", `${resource} was not found.`);
91
- }
92
- function invalidArgument(message2) {
93
- throw new GmailError(400, "invalidArgument", message2);
94
- }
95
- function unsupported(message2) {
96
- throw new GmailError(501, "notImplemented", message2);
97
- }
98
- function googleStatus(status) {
99
- if (status === 400)
100
- return "INVALID_ARGUMENT";
101
- if (status === 401)
102
- return "UNAUTHENTICATED";
103
- if (status === 403)
104
- return "PERMISSION_DENIED";
105
- if (status === 404)
106
- return "NOT_FOUND";
107
- if (status === 429)
108
- return "RESOURCE_EXHAUSTED";
109
- if (status === 409)
110
- return "ALREADY_EXISTS";
111
- if (status === 501)
112
- return "UNIMPLEMENTED";
113
- return "INTERNAL";
114
- }
115
- var KNOWN_FAULT_NAMES = ["rate-limited"];
116
- var gmailFaultSchema = z.object({
117
- name: z.enum(KNOWN_FAULT_NAMES),
118
- target: z.string().min(1).max(128).default("messages.send"),
119
- succeedFirst: z.number().int().nonnegative().max(1e3).default(2),
120
- throttleFor: z.number().int().positive().max(1e3).default(3),
121
- retryAfterSeconds: z.number().int().positive().max(3600).default(1)
122
- }).strict();
123
- function checkFault(db, operation) {
124
- const fault = readFaults(db).find((f) => f.target === operation);
125
- if (!fault)
126
- return;
127
- const calls = bumpFaultCounter(db, operation);
128
- if (calls > fault.succeedFirst && calls <= fault.succeedFirst + fault.throttleFor) {
129
- throw new GmailError(429, "rateLimitExceeded", `Rate limit exceeded for ${operation}. Retry after ${fault.retryAfterSeconds}s.`);
130
- }
131
- }
132
- function readFaults(db) {
133
- const row = db.prepare("SELECT value FROM gmail_config WHERE key = 'faults'").get();
134
- if (!row)
135
- return [];
136
- try {
137
- return JSON.parse(row.value);
138
- } catch {
139
- return [];
140
- }
141
- }
142
- function bumpFaultCounter(db, operation) {
143
- db.prepare("INSERT INTO fault_counters(operation, calls) VALUES (?, 1) ON CONFLICT(operation) DO UPDATE SET calls = calls + 1").run(operation);
144
- const row = db.prepare("SELECT calls FROM fault_counters WHERE operation = ?").get(operation);
145
- return row.calls;
146
- }
147
-
148
- // ../packages/twin-gmail/dist/src/search-parse.js
149
- var MAX_QUERY_BYTES = 4096;
150
- var MAX_TOKENS = 256;
151
- var MAX_DEPTH = 20;
152
- var MAX_BRANCHES = 256;
153
- var SEARCH_MAILBOX_MESSAGE_BUDGET = 1e4;
154
- var KNOWN_FIELDS = /* @__PURE__ */ new Set([
155
- "from",
156
- "to",
157
- "cc",
158
- "bcc",
159
- "deliveredto",
160
- "list",
161
- "subject",
162
- "rfc822msgid",
163
- "filename",
164
- "after",
165
- "newer",
166
- "before",
167
- "older",
168
- "newer_than",
169
- "older_than",
170
- "size",
171
- "larger",
172
- "smaller",
173
- "label",
174
- "category",
175
- "in",
176
- "is",
177
- "has"
178
- ]);
179
- var CATEGORY_LABELS = {
180
- primary: "CATEGORY_PERSONAL",
181
- personal: "CATEGORY_PERSONAL",
182
- social: "CATEGORY_SOCIAL",
183
- promotions: "CATEGORY_PROMOTIONS",
184
- updates: "CATEGORY_UPDATES",
185
- forums: "CATEGORY_FORUMS"
186
- };
187
- function validateSearchQuery(query) {
188
- const node = parseSearchQuery(query);
189
- const walk = (current) => {
190
- switch (current.type) {
191
- case "and":
192
- case "or":
193
- for (const child of current.children)
194
- walk(child);
195
- return;
196
- case "not":
197
- walk(current.child);
198
- return;
199
- case "around":
200
- return;
201
- case "term":
202
- assertSearchTerm(current.field, current.value);
203
- return;
204
- }
205
- };
206
- walk(node);
207
- return node;
208
- }
209
- function assertSearchTerm(field, value) {
210
- if (!field)
211
- return;
212
- if (!KNOWN_FIELDS.has(field)) {
213
- invalidArgument(`Unsupported search operator: ${field}`);
214
- }
215
- if (field === "category") {
216
- if (!CATEGORY_LABELS[value.toLocaleLowerCase("en-US")]) {
217
- invalidArgument(`Unsupported search category: ${value}`);
218
- }
219
- return;
220
- }
221
- if (field === "after" || field === "newer" || field === "before" || field === "older") {
222
- parseDate(value);
223
- return;
224
- }
225
- if (field === "newer_than" || field === "older_than") {
226
- parseDuration(value);
227
- return;
228
- }
229
- if (field === "size" || field === "larger" || field === "smaller") {
230
- parseSize(value);
231
- return;
232
- }
233
- if (field === "has") {
234
- assertHasOperator(value);
235
- }
236
- }
237
- function parseSearchQuery(query) {
238
- if (Buffer.byteLength(query) > MAX_QUERY_BYTES)
239
- invalidArgument("Search query exceeds limit");
240
- const tokens = tokenize(query);
241
- if (tokens.length > MAX_TOKENS)
242
- invalidArgument("Search query has too many tokens");
243
- if (tokens.length === 0)
244
- return { type: "and", children: [] };
245
- let position = 0;
246
- const parseExpression = (depth, stop) => {
247
- if (depth > MAX_DEPTH)
248
- invalidArgument("Search query nesting exceeds limit");
249
- const alternatives = [];
250
- let conjunction = [];
251
- const flush = () => {
252
- alternatives.push(conjunction.length === 1 ? conjunction[0] : { type: "and", children: conjunction });
253
- conjunction = [];
254
- };
255
- while (position < tokens.length) {
256
- const token = tokens[position];
257
- if (stop && token.value === stop)
258
- break;
259
- if (token.value.toUpperCase() === "OR") {
260
- position++;
261
- if (!conjunction.length)
262
- invalidArgument("OR requires a left expression");
263
- flush();
264
- continue;
265
- }
266
- if (token.value.toUpperCase() === "AND") {
267
- position++;
268
- continue;
269
- }
270
- conjunction.push(parsePrimary(depth + 1));
271
- }
272
- if (conjunction.length)
273
- flush();
274
- if (!alternatives.length)
275
- return { type: "and", children: [] };
276
- return alternatives.length === 1 ? alternatives[0] : { type: "or", children: alternatives };
277
- };
278
- const parsePrimary = (depth) => {
279
- const token = tokens[position++];
280
- if (token.value === "(" || token.value === "{") {
281
- const end = token.value === "(" ? ")" : "}";
282
- const child = parseExpression(depth, end);
283
- if (tokens[position]?.value !== end)
284
- invalidArgument(`Unclosed ${token.value}`);
285
- position++;
286
- return token.value === "{" ? makeImplicitOr(child) : child;
287
- }
288
- if (token.value === ")" || token.value === "}")
289
- invalidArgument(`Unexpected ${token.value}`);
290
- let value = token.value;
291
- let negate = false;
292
- if (value === "-") {
293
- negate = true;
294
- const next = tokens[position++];
295
- if (!next)
296
- invalidArgument("Negation requires an expression");
297
- position--;
298
- const child = parsePrimary(depth + 1);
299
- return { type: "not", child };
300
- }
301
- if (value.startsWith("-") && value.length > 1) {
302
- negate = true;
303
- value = value.slice(1);
304
- }
305
- const around = tokens[position]?.value.toUpperCase() === "AROUND";
306
- if (around) {
307
- position++;
308
- const distance = Number(tokens[position++]?.value);
309
- const right = tokens[position++]?.value;
310
- if (!Number.isInteger(distance) || distance < 1 || distance > 100 || !right) {
311
- invalidArgument("AROUND requires a distance and right term");
312
- }
313
- const node2 = { type: "around", left: value, right, distance };
314
- return negate ? { type: "not", child: node2 } : node2;
315
- }
316
- const colon = value.indexOf(":");
317
- let node;
318
- if (colon > 0) {
319
- const field = value.slice(0, colon).toLowerCase();
320
- let fieldValue = value.slice(colon + 1);
321
- if (!fieldValue && (tokens[position]?.value === "(" || tokens[position]?.value === "{")) {
322
- node = parseFieldGroup(field, depth + 1);
323
- return negate ? { type: "not", child: node } : node;
324
- }
325
- if (!fieldValue && tokens[position] && !["AND", "OR", ")", "}"].includes(tokens[position].value.toUpperCase())) {
326
- fieldValue = tokens[position++].value;
327
- }
328
- node = { type: "term", field, value: fieldValue, exact: token.quoted };
329
- } else {
330
- node = { type: "term", value: value.startsWith("+") ? value.slice(1) : value, exact: token.quoted || value.startsWith("+") };
331
- }
332
- return negate ? { type: "not", child: node } : node;
333
- };
334
- const parseFieldGroup = (field, depth) => {
335
- if (depth > MAX_DEPTH)
336
- invalidArgument("Search query nesting exceeds limit");
337
- const opening = tokens[position++].value;
338
- const end = opening === "(" ? ")" : "}";
339
- const groups = [[]];
340
- while (position < tokens.length && tokens[position].value !== end) {
341
- const token = tokens[position++];
342
- if (token.value.toUpperCase() === "OR") {
343
- groups.push([]);
344
- continue;
345
- }
346
- if (token.value.toUpperCase() === "AND")
347
- continue;
348
- if (["(", "{", ")", "}"].includes(token.value))
349
- invalidArgument("Nested field groups are unsupported");
350
- let value = token.value;
351
- let negate = false;
352
- if (value.startsWith("-")) {
353
- negate = true;
354
- value = value.slice(1);
355
- }
356
- const term = { type: "term", field, value, exact: token.quoted };
357
- groups.at(-1).push(negate ? { type: "not", child: term } : term);
358
- }
359
- if (tokens[position]?.value !== end)
360
- invalidArgument(`Unclosed ${opening}`);
361
- position++;
362
- const nodes = groups.map((children) => children.length === 1 ? children[0] : { type: "and", children });
363
- const useOr = opening === "{" || nodes.length > 1;
364
- return useOr ? { type: "or", children: nodes } : nodes[0] ?? { type: "and", children: [] };
365
- };
366
- const root = parseExpression(0);
367
- if (position !== tokens.length)
368
- invalidArgument("Unexpected search token");
369
- if (countBranches(root) > MAX_BRANCHES)
370
- invalidArgument("Search query has too many branches");
371
- return root;
372
- }
373
- function assertHasOperator(value) {
374
- const normalized = value.toLocaleLowerCase("en-US");
375
- if (normalized.endsWith("-star")) {
376
- invalidArgument(`Unsupported colored-star operator: has:${value}; twin maps only STARRED via is:starred`);
377
- }
378
- }
379
- function parseDate(value) {
380
- const normalized = /^\d{4}\/\d{1,2}\/\d{1,2}$/.test(value) ? value.replaceAll("/", "-") : value;
381
- const date = Date.parse(`${normalized}${/^\d{4}-\d/.test(normalized) ? "T00:00:00Z" : ""}`);
382
- if (Number.isNaN(date))
383
- invalidArgument(`Invalid search date: ${value}`);
384
- return date;
385
- }
386
- function parseDuration(value) {
387
- const match = value.match(/^(\d+)([dmy])$/i);
388
- if (!match)
389
- invalidArgument(`Invalid search duration: ${value}`);
390
- const units = { d: 864e5, m: 30 * 864e5, y: 365 * 864e5 };
391
- return Number(match[1]) * units[match[2].toLowerCase()];
392
- }
393
- function parseSize(value) {
394
- const match = value.match(/^(\d+(?:\.\d+)?)([kmg])?$/i);
395
- if (!match)
396
- invalidArgument(`Invalid search size: ${value}`);
397
- const scale = { k: 1024, m: 1024 ** 2, g: 1024 ** 3 };
398
- return Math.floor(Number(match[1]) * (match[2] ? scale[match[2].toLowerCase()] : 1));
399
- }
400
- function countBranches(node) {
401
- switch (node.type) {
402
- case "and":
403
- case "or":
404
- return node.children.reduce((sum, child) => sum + countBranches(child), 0);
405
- case "not":
406
- return countBranches(node.child);
407
- case "around":
408
- case "term":
409
- return 1;
410
- }
411
- }
412
- function tokenize(query) {
413
- const out = [];
414
- let index = 0;
415
- while (index < query.length) {
416
- if (/\s/.test(query[index])) {
417
- index++;
418
- continue;
419
- }
420
- const char = query[index];
421
- if ("(){}".includes(char)) {
422
- out.push({ value: char });
423
- index++;
424
- continue;
425
- }
426
- let value = "";
427
- let quoted = false;
428
- while (index < query.length && !/\s/.test(query[index]) && !"(){}".includes(query[index])) {
429
- if (query[index] === '"') {
430
- quoted = true;
431
- index++;
432
- while (index < query.length && query[index] !== '"') {
433
- if (query[index] === "\\" && index + 1 < query.length)
434
- index++;
435
- value += query[index++];
436
- }
437
- if (query[index] !== '"')
438
- invalidArgument("Unclosed search quote");
439
- index++;
440
- } else {
441
- value += query[index++];
442
- }
443
- }
444
- if (value)
445
- out.push({ value, quoted });
446
- }
447
- return out;
448
- }
449
- function makeImplicitOr(node) {
450
- return node.type === "and" ? { type: "or", children: node.children } : node;
451
- }
452
-
453
- // ../packages/twin-gmail/dist/src/seed.js
454
- var email = z.string().trim().email().transform((value) => value.toLowerCase());
455
- var id = z.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/);
456
- var attachmentSchema = z.object({
457
- filename: z.string().max(512),
458
- mimeType: z.string().min(1).max(255).default("application/octet-stream"),
459
- disposition: z.enum(["attachment", "inline"]).default("attachment"),
460
- contentId: z.string().max(998).optional(),
461
- data: z.string().max(5e7)
462
- }).strict();
463
- var messageFields = {
464
- id: id.optional(),
465
- threadId: id.optional(),
466
- raw: z.string().max(5e7).optional(),
467
- from: email.optional(),
468
- to: z.array(email).max(500).default([]),
469
- cc: z.array(email).max(500).default([]),
470
- bcc: z.array(email).max(500).default([]),
471
- subject: z.string().max(998).default(""),
472
- text: z.string().max(25e6).default(""),
473
- html: z.string().max(25e6).default(""),
474
- date: z.string().datetime({ offset: true }).optional(),
475
- messageId: z.string().min(3).max(998).optional(),
476
- inReplyTo: z.string().max(998).optional(),
477
- references: z.array(z.string().max(998)).max(100).default([]),
478
- attachments: z.array(attachmentSchema).max(100).default([])
479
- };
480
- var messageSchema = z.object({
481
- ...messageFields,
482
- labels: z.array(z.string().min(1).max(255)).max(100).default([])
483
- }).strict();
484
- var draftSchema = z.object(messageFields).strict();
485
- var labelSchema = z.object({
486
- id: id.optional(),
487
- name: z.string().trim().min(1).max(225),
488
- color: z.object({
489
- textColor: z.string().max(32).optional(),
490
- backgroundColor: z.string().max(32).optional()
491
- }).strict().optional()
492
- }).strict();
493
- var filterSchema = z.object({
494
- id: id.optional(),
495
- criteria: z.object({
496
- from: z.string().max(998).optional(),
497
- to: z.string().max(998).optional(),
498
- subject: z.string().max(998).optional(),
499
- query: z.string().max(4096).optional(),
500
- negatedQuery: z.string().max(4096).optional(),
501
- hasAttachment: z.boolean().optional(),
502
- excludeChats: z.boolean().optional(),
503
- size: z.number().int().nonnegative().optional(),
504
- sizeComparison: z.enum(["larger", "smaller"]).optional()
505
- }).strict().default({}),
506
- action: z.object({
507
- addLabelIds: z.array(z.string().min(1)).max(100).default([]),
508
- removeLabelIds: z.array(z.string().min(1)).max(100).default([]),
509
- forward: email.optional()
510
- }).strict().default({ addLabelIds: [], removeLabelIds: [] })
511
- }).strict();
512
- var sendAsSchema = z.object({
513
- sendAsEmail: email,
514
- displayName: z.string().max(256).default(""),
515
- replyToAddress: email.optional(),
516
- isPrimary: z.boolean().default(false),
517
- isDefault: z.boolean().default(false),
518
- verificationStatus: z.enum(["accepted", "pending"]).default("accepted")
519
- }).strict();
520
- var mailboxSchema = z.object({
521
- email,
522
- displayName: z.string().max(256).default(""),
523
- labels: z.array(labelSchema).max(5e3).default([]),
524
- messages: z.array(messageSchema).max(1e4).default([]),
525
- drafts: z.array(draftSchema).max(5e3).default([]),
526
- filters: z.array(filterSchema).max(1e3).default([]),
527
- forwardingAddresses: z.array(z.object({
528
- forwardingEmail: email,
529
- verificationStatus: z.enum(["accepted", "pending"]).default("pending")
530
- }).strict()).max(1e3).default([]),
531
- sendAs: z.array(sendAsSchema).max(1e3).default([])
532
- }).strict();
533
- var gmailSeedSchema = z.object({
534
- primaryMailbox: mailboxSchema,
535
- mailboxes: z.array(mailboxSchema).max(100).default([]),
536
- deliveryMode: z.enum(["sender-only", "seeded-mailboxes"]).default("sender-only"),
537
- clock: z.string().datetime({ offset: true }).default("2025-01-01T00:00:00.000Z"),
538
- faults: z.array(gmailFaultSchema).max(50).default([])
539
- }).strict().superRefine((seed, ctx) => {
540
- const emails = [seed.primaryMailbox.email, ...seed.mailboxes.map((mailbox) => mailbox.email)];
541
- const seen = /* @__PURE__ */ new Set();
542
- for (const mailboxEmail of emails) {
543
- if (seen.has(mailboxEmail)) {
544
- ctx.addIssue({ code: "custom", message: `Duplicate mailbox: ${mailboxEmail}` });
545
- }
546
- seen.add(mailboxEmail);
547
- }
548
- for (const mailbox of [seed.primaryMailbox, ...seed.mailboxes]) {
549
- const labelNames = /* @__PURE__ */ new Set();
550
- for (const label2 of mailbox.labels) {
551
- const key = label2.name.toLowerCase();
552
- if (labelNames.has(key)) {
553
- ctx.addIssue({ code: "custom", message: `Duplicate label in ${mailbox.email}: ${label2.name}` });
554
- }
555
- labelNames.add(key);
556
- }
557
- for (const filter2 of mailbox.filters) {
558
- if (filter2.action.forward) {
559
- ctx.addIssue({
560
- code: "custom",
561
- message: `Filter forwarding is unsupported: ${mailbox.email}`
562
- });
563
- }
564
- for (const key of ["query", "negatedQuery"]) {
565
- const value = filter2.criteria[key];
566
- if (!value)
567
- continue;
568
- try {
569
- validateSearchQuery(value);
570
- } catch (error) {
571
- ctx.addIssue({
572
- code: "custom",
573
- message: `Invalid filter ${key} in ${mailbox.email}: ${error.message}`
574
- });
575
- }
576
- }
577
- }
578
- }
579
- });
580
- function parseSeed(input) {
581
- return gmailSeedSchema.parse(input);
582
- }
583
- function loadSeedFromEnv(env = process.env) {
584
- const raw = env.POME_SEED_JSON;
585
- if (!raw)
586
- return parseSeed(defaultSeedState());
587
- let parsed;
588
- try {
589
- parsed = JSON.parse(raw);
590
- } catch (error) {
591
- throw new Error(`POME_SEED_JSON is not valid JSON: ${error.message}`);
592
- }
593
- return parseSeed(parsed);
594
- }
595
- var DEFAULT_GMAIL_AGENT_EMAIL = "pome-agent@pome-twin.test";
596
- function agentPathInboxMailbox(email3 = DEFAULT_GMAIL_AGENT_EMAIL) {
597
- return {
598
- email: email3,
599
- displayName: "Pome Agent",
600
- labels: [
601
- { id: "Label_follow_up", name: "Follow Up" },
602
- { id: "Label_build", name: "Build" }
603
- ],
604
- messages: [
605
- {
606
- id: "msg_welcome",
607
- threadId: "thread_welcome",
608
- from: "welcome@pome-twin.test",
609
- to: [email3],
610
- subject: "Welcome to your Pome Gmail twin",
611
- text: "Your deterministic inbox is ready for agent testing.",
612
- html: "<p>Your deterministic inbox is ready for agent testing.</p>",
613
- date: "2026-07-18T09:00:00.000Z",
614
- messageId: "welcome@pome-twin.test",
615
- labels: ["INBOX"]
616
- },
617
- {
618
- id: "msg_build",
619
- threadId: "thread_build",
620
- from: "ci@example.com",
621
- to: [email3],
622
- subject: "Build failed on main",
623
- text: "The nightly build failed. See the attached log.",
624
- date: "2026-07-19T10:00:00.000Z",
625
- messageId: "build-001@example.com",
626
- labels: ["INBOX", "UNREAD", "Build"],
627
- attachments: [
628
- {
629
- filename: "build.log",
630
- mimeType: "text/plain",
631
- data: Buffer.from("BUILD FAILED step=test\n", "utf8").toString("base64")
632
- }
633
- ]
634
- },
635
- {
636
- id: "msg_build_reply",
637
- threadId: "thread_build",
638
- from: email3,
639
- to: ["ci@example.com"],
640
- subject: "Re: Build failed on main",
641
- text: "Looking into the failure now.",
642
- date: "2026-07-19T11:00:00.000Z",
643
- messageId: "build-reply@pome-twin.test",
644
- inReplyTo: "build-001@example.com",
645
- references: ["build-001@example.com"],
646
- labels: ["SENT"]
647
- },
648
- {
649
- id: "msg_support",
650
- threadId: "thread_support",
651
- from: "alice@example.com",
652
- to: [email3],
653
- subject: "Production export is stuck",
654
- text: "Our production export has been stuck for an hour. Can you investigate?",
655
- date: "2026-07-19T12:00:00.000Z",
656
- messageId: "support-001@example.com",
657
- labels: ["INBOX", "UNREAD"]
658
- }
659
- ],
660
- drafts: [
661
- {
662
- id: "draft_ack",
663
- threadId: "thread_draft_ack",
664
- to: ["bob@example.com"],
665
- subject: "Draft acknowledgment",
666
- text: "Thanks \u2014 I'll follow up shortly.",
667
- date: "2026-07-19T13:00:00.000Z",
668
- messageId: "draft-ack@pome-twin.test"
669
- }
670
- ],
671
- filters: [],
672
- forwardingAddresses: [],
673
- sendAs: []
674
- };
675
- }
676
- function defaultSeedState() {
677
- return {
678
- primaryMailbox: agentPathInboxMailbox(),
679
- mailboxes: [],
680
- deliveryMode: "sender-only",
681
- clock: "2026-07-20T00:00:00.000Z"
682
- };
683
- }
9
+ import { z } from 'zod';
684
10
 
685
11
  // ../packages/twin-gmail/dist/src/db.js
686
12
  var MIGRATION_SQL = `
@@ -1104,7 +430,7 @@ var MAX_RAW_BYTES = 36700160;
1104
430
  var MAX_HEADERS = 1e3;
1105
431
  var MAX_HEADER_BYTES = 256 * 1024;
1106
432
  var MAX_PARTS = 500;
1107
- var MAX_DEPTH2 = 20;
433
+ var MAX_DEPTH = 20;
1108
434
  function canonicalRaw(input) {
1109
435
  const raw = typeof input === "string" ? Buffer.from(input, "utf8") : Buffer.from(input);
1110
436
  if (raw.length === 0)
@@ -1263,7 +589,7 @@ function parseHeaders(raw) {
1263
589
  });
1264
590
  }
1265
591
  function parsePart(body, headers2, contentType, state, depth) {
1266
- if (depth > MAX_DEPTH2 || ++state.parts > MAX_PARTS)
592
+ if (depth > MAX_DEPTH || ++state.parts > MAX_PARTS)
1267
593
  invalidArgument("MIME nesting/part limit exceeded");
1268
594
  if (contentType.type.startsWith("multipart/")) {
1269
595
  const boundary = contentType.params.boundary;
@@ -1589,16 +915,16 @@ var SYSTEM_LABELS = [
1589
915
  ["CATEGORY_UPDATES", "CATEGORY_UPDATES"],
1590
916
  ["CATEGORY_FORUMS", "CATEGORY_FORUMS"]
1591
917
  ];
1592
- function createMailbox(db, email3, displayName, createdAt) {
1593
- const result = db.prepare("INSERT INTO mailboxes(email, display_name, created_at) VALUES (?, ?, ?)").run(email3, displayName, createdAt);
918
+ function createMailbox(db, email2, displayName, createdAt) {
919
+ const result = db.prepare("INSERT INTO mailboxes(email, display_name, created_at) VALUES (?, ?, ?)").run(email2, displayName, createdAt);
1594
920
  const mailboxId = Number(result.lastInsertRowid);
1595
921
  db.prepare("INSERT INTO mailbox_counters(mailbox_id) VALUES (?)").run(mailboxId);
1596
- for (const [id2, name] of SYSTEM_LABELS) {
1597
- db.prepare("INSERT INTO labels(mailbox_id, id, name, type, text_color, background_color) VALUES (?, ?, ?, 'system', NULL, NULL)").run(mailboxId, id2, name);
922
+ for (const [id, name] of SYSTEM_LABELS) {
923
+ db.prepare("INSERT INTO labels(mailbox_id, id, name, type, text_color, background_color) VALUES (?, ?, ?, 'system', NULL, NULL)").run(mailboxId, id, name);
1598
924
  }
1599
925
  db.prepare(`INSERT INTO send_as(
1600
926
  mailbox_id, email, display_name, is_primary, is_default, verification_status
1601
- ) VALUES (?, ?, ?, 1, 1, 'accepted')`).run(mailboxId, email3, displayName);
927
+ ) VALUES (?, ?, ?, 1, 1, 'accepted')`).run(mailboxId, email2, displayName);
1602
928
  return mailboxId;
1603
929
  }
1604
930
  function nextId(db, mailboxId, counter, prefix) {
@@ -1612,17 +938,17 @@ function nextTimestamp(db, mailboxId) {
1612
938
  const config = db.prepare("SELECT value FROM gmail_config WHERE key = 'clock'").get();
1613
939
  return new Date(Date.parse(config.value) + row.logical_clock * 1e3).toISOString();
1614
940
  }
1615
- function addHistory(db, mailboxId, messageId2, threadId, eventType, labelIds2 = []) {
1616
- const id2 = nextId(db, mailboxId, "history_counter", "history").slice("history_".length);
1617
- const numeric = Number.parseInt(id2, 16);
941
+ function addHistory(db, mailboxId, messageId, threadId, eventType, labelIds2 = []) {
942
+ const id = nextId(db, mailboxId, "history_counter", "history").slice("history_".length);
943
+ const numeric = Number.parseInt(id, 16);
1618
944
  const timestamp = nextTimestamp(db, mailboxId);
1619
945
  db.prepare(`INSERT INTO history(mailbox_id, id, message_id, thread_id, event_type, label_ids_json, created_at)
1620
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(mailboxId, numeric, messageId2, threadId, eventType, JSON.stringify([...labelIds2].sort()), timestamp);
946
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(mailboxId, numeric, messageId, threadId, eventType, JSON.stringify([...labelIds2].sort()), timestamp);
1621
947
  return String(numeric);
1622
948
  }
1623
949
  function insertStoredMessage(db, mailboxId, raw, options = {}) {
1624
950
  const parsed = parseMime(raw);
1625
- const messageId2 = options.id ?? nextId(db, mailboxId, "message_counter", "msg");
951
+ const messageId = options.id ?? nextId(db, mailboxId, "message_counter", "msg");
1626
952
  const subjectKey = normalizeSubject(parsed.subject);
1627
953
  const threadId = options.forceThreadId && options.threadId ? options.threadId : resolveThread(db, mailboxId, options.threadId, subjectKey, [
1628
954
  ...parsed.inReplyTo ? [parsed.inReplyTo] : [],
@@ -1639,32 +965,32 @@ function insertStoredMessage(db, mailboxId, raw, options = {}) {
1639
965
  mailbox_id, id, thread_id, rfc_message_id, internal_date, sent_at,
1640
966
  from_address, to_json, cc_json, bcc_json, delivered_to, subject,
1641
967
  normalized_subject, snippet, text_body, html_body, headers_json, size_estimate
1642
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(mailboxId, messageId2, threadId, parsed.messageId || `${messageId2}@pome-twin.test`, internalDate, date, parsed.from, JSON.stringify(parsed.to), JSON.stringify(parsed.cc), JSON.stringify(parsed.bcc), options.deliveredTo ?? parsed.deliveredTo, parsed.subject, subjectKey, snippet, parsed.text, parsed.html, JSON.stringify(parsed.headers), raw.byteLength);
1643
- db.prepare("INSERT INTO message_blobs(mailbox_id, message_id, raw, sha256, size) VALUES (?, ?, ?, ?, ?)").run(mailboxId, messageId2, Buffer.from(raw), mimeSha256(raw), raw.byteLength);
968
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(mailboxId, messageId, threadId, parsed.messageId || `${messageId}@pome-twin.test`, internalDate, date, parsed.from, JSON.stringify(parsed.to), JSON.stringify(parsed.cc), JSON.stringify(parsed.bcc), options.deliveredTo ?? parsed.deliveredTo, parsed.subject, subjectKey, snippet, parsed.text, parsed.html, JSON.stringify(parsed.headers), raw.byteLength);
969
+ db.prepare("INSERT INTO message_blobs(mailbox_id, message_id, raw, sha256, size) VALUES (?, ?, ?, ?, ?)").run(mailboxId, messageId, Buffer.from(raw), mimeSha256(raw), raw.byteLength);
1644
970
  parsed.attachments.forEach((attachment2, index) => {
1645
971
  const attachmentId = nextId(db, mailboxId, "attachment_counter", "att");
1646
972
  db.prepare(`INSERT INTO attachments(
1647
973
  mailbox_id, message_id, id, part_index, filename, mime_type, disposition,
1648
974
  content_id, sha256, size, data
1649
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(mailboxId, messageId2, attachmentId, index, attachment2.filename, attachment2.mimeType, attachment2.disposition, attachment2.contentId ?? null, mimeSha256(attachment2.data), attachment2.data.byteLength, attachment2.data);
975
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(mailboxId, messageId, attachmentId, index, attachment2.filename, attachment2.mimeType, attachment2.disposition, attachment2.contentId ?? null, mimeSha256(attachment2.data), attachment2.data.byteLength, attachment2.data);
1650
976
  });
1651
977
  const labels2 = options.draft ? ["DRAFT"] : [...new Set(options.labels ?? [])];
1652
978
  assertLabels(db, mailboxId, labels2);
1653
979
  for (const label2 of labels2) {
1654
- db.prepare("INSERT INTO message_labels(mailbox_id, message_id, label_id) VALUES (?, ?, ?)").run(mailboxId, messageId2, label2);
980
+ db.prepare("INSERT INTO message_labels(mailbox_id, message_id, label_id) VALUES (?, ?, ?)").run(mailboxId, messageId, label2);
1655
981
  }
1656
982
  db.prepare("UPDATE threads SET updated_at = ? WHERE mailbox_id = ? AND id = ?").run(date, mailboxId, threadId);
1657
983
  if (options.recordHistory !== false)
1658
- addHistory(db, mailboxId, messageId2, threadId, "messageAdded", labels2);
1659
- return semanticMessage(db, mailboxId, messageId2);
984
+ addHistory(db, mailboxId, messageId, threadId, "messageAdded", labels2);
985
+ return semanticMessage(db, mailboxId, messageId);
1660
986
  }
1661
- function semanticMessage(db, mailboxId, messageId2) {
1662
- const row = db.prepare("SELECT * FROM messages WHERE mailbox_id = ? AND id = ?").get(mailboxId, messageId2);
987
+ function semanticMessage(db, mailboxId, messageId) {
988
+ const row = db.prepare("SELECT * FROM messages WHERE mailbox_id = ? AND id = ?").get(mailboxId, messageId);
1663
989
  if (!row)
1664
990
  notFound("Message");
1665
- const labels2 = db.prepare("SELECT label_id FROM message_labels WHERE mailbox_id = ? AND message_id = ? ORDER BY label_id").all(mailboxId, messageId2);
991
+ const labels2 = db.prepare("SELECT label_id FROM message_labels WHERE mailbox_id = ? AND message_id = ? ORDER BY label_id").all(mailboxId, messageId);
1666
992
  const attachments = db.prepare(`SELECT id, filename, mime_type, disposition, content_id, sha256, size
1667
- FROM attachments WHERE mailbox_id = ? AND message_id = ? ORDER BY part_index`).all(mailboxId, messageId2);
993
+ FROM attachments WHERE mailbox_id = ? AND message_id = ? ORDER BY part_index`).all(mailboxId, messageId);
1668
994
  return {
1669
995
  id: row.id,
1670
996
  threadId: row.thread_id,
@@ -1691,8 +1017,8 @@ function semanticMessage(db, mailboxId, messageId2) {
1691
1017
  }))
1692
1018
  };
1693
1019
  }
1694
- function rawMessage(db, mailboxId, messageId2) {
1695
- const row = db.prepare("SELECT raw FROM message_blobs WHERE mailbox_id = ? AND message_id = ?").get(mailboxId, messageId2);
1020
+ function rawMessage(db, mailboxId, messageId) {
1021
+ const row = db.prepare("SELECT raw FROM message_blobs WHERE mailbox_id = ? AND message_id = ?").get(mailboxId, messageId);
1696
1022
  if (!row)
1697
1023
  notFound("Message");
1698
1024
  return Buffer.from(row.raw);
@@ -1733,37 +1059,37 @@ function seedMailbox(db, mailbox) {
1733
1059
  db.prepare("INSERT INTO labels(mailbox_id, id, name, type, text_color, background_color) VALUES (?, ?, ?, 'user', ?, ?)").run(mailboxId, labelId, label2.name, label2.color?.textColor ?? null, label2.color?.backgroundColor ?? null);
1734
1060
  labelByName.set(label2.name.toLowerCase(), labelId);
1735
1061
  }
1736
- for (const message2 of mailbox.messages) {
1737
- if (message2.id)
1062
+ for (const message of mailbox.messages) {
1063
+ if (message.id)
1738
1064
  nextId(db, mailboxId, "message_counter", "msg");
1739
- if (message2.threadId)
1065
+ if (message.threadId)
1740
1066
  nextId(db, mailboxId, "thread_counter", "thread");
1741
- const raw = seedRaw(db, mailbox, message2, mailboxId);
1742
- const labels2 = message2.labels.map((label2) => labelByName.get(label2.toLowerCase()) ?? label2);
1067
+ const raw = seedRaw(db, mailbox, message, mailboxId);
1068
+ const labels2 = message.labels.map((label2) => labelByName.get(label2.toLowerCase()) ?? label2);
1743
1069
  insertStoredMessage(db, mailboxId, raw, {
1744
- id: message2.id,
1745
- threadId: message2.threadId,
1070
+ id: message.id,
1071
+ threadId: message.threadId,
1746
1072
  labels: labels2,
1747
1073
  recordHistory: false,
1748
- forceThreadId: message2.threadId !== void 0
1074
+ forceThreadId: message.threadId !== void 0
1749
1075
  });
1750
1076
  }
1751
- for (const draft3 of mailbox.drafts) {
1752
- if (draft3.id)
1077
+ for (const draft2 of mailbox.drafts) {
1078
+ if (draft2.id)
1753
1079
  nextId(db, mailboxId, "draft_counter", "draft");
1754
- if (draft3.threadId)
1080
+ if (draft2.threadId)
1755
1081
  nextId(db, mailboxId, "thread_counter", "thread");
1756
- const raw = seedRaw(db, mailbox, draft3, mailboxId);
1757
- const message2 = insertStoredMessage(db, mailboxId, raw, {
1758
- id: draft3.id ? `${draft3.id}_message` : void 0,
1759
- threadId: draft3.threadId,
1082
+ const raw = seedRaw(db, mailbox, draft2, mailboxId);
1083
+ const message = insertStoredMessage(db, mailboxId, raw, {
1084
+ id: draft2.id ? `${draft2.id}_message` : void 0,
1085
+ threadId: draft2.threadId,
1760
1086
  draft: true,
1761
1087
  recordHistory: false,
1762
- forceThreadId: draft3.threadId !== void 0
1088
+ forceThreadId: draft2.threadId !== void 0
1763
1089
  });
1764
- const draftId = draft3.id ?? nextId(db, mailboxId, "draft_counter", "draft");
1765
- const date = new Date(message2.internalDate).toISOString();
1766
- db.prepare("INSERT INTO drafts(mailbox_id, id, message_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(mailboxId, draftId, message2.id, date, date);
1090
+ const draftId = draft2.id ?? nextId(db, mailboxId, "draft_counter", "draft");
1091
+ const date = new Date(message.internalDate).toISOString();
1092
+ db.prepare("INSERT INTO drafts(mailbox_id, id, message_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(mailboxId, draftId, message.id, date, date);
1767
1093
  }
1768
1094
  seedSettings(db, mailboxId, mailbox);
1769
1095
  }
@@ -1779,29 +1105,29 @@ function seedSettings(db, mailboxId, mailbox) {
1779
1105
  for (const filter2 of mailbox.filters) {
1780
1106
  if (filter2.id)
1781
1107
  nextId(db, mailboxId, "filter_counter", "filter");
1782
- const id2 = filter2.id ?? nextId(db, mailboxId, "filter_counter", "filter");
1783
- db.prepare("INSERT INTO filters(mailbox_id, id, criteria_json, action_json) VALUES (?, ?, ?, ?)").run(mailboxId, id2, JSON.stringify(filter2.criteria), JSON.stringify(filter2.action));
1108
+ const id = filter2.id ?? nextId(db, mailboxId, "filter_counter", "filter");
1109
+ db.prepare("INSERT INTO filters(mailbox_id, id, criteria_json, action_json) VALUES (?, ?, ?, ?)").run(mailboxId, id, JSON.stringify(filter2.criteria), JSON.stringify(filter2.action));
1784
1110
  }
1785
1111
  }
1786
- function seedRaw(db, mailbox, message2, mailboxId) {
1787
- if (message2.raw !== void 0) {
1788
- return message2.raw.includes("\n") ? Buffer.from(message2.raw, "utf8") : decodeGmailRaw(message2.raw);
1112
+ function seedRaw(db, mailbox, message, mailboxId) {
1113
+ if (message.raw !== void 0) {
1114
+ return message.raw.includes("\n") ? Buffer.from(message.raw, "utf8") : decodeGmailRaw(message.raw);
1789
1115
  }
1790
- const date = message2.date ?? nextTimestamp(db, mailboxId);
1791
- const id2 = message2.messageId ?? `${nextId(db, mailboxId, "message_counter", "rfc")}@pome-twin.test`;
1116
+ const date = message.date ?? nextTimestamp(db, mailboxId);
1117
+ const id = message.messageId ?? `${nextId(db, mailboxId, "message_counter", "rfc")}@pome-twin.test`;
1792
1118
  return composeMime({
1793
- from: message2.from ?? mailbox.email,
1794
- to: message2.to,
1795
- cc: message2.cc,
1796
- bcc: message2.bcc,
1797
- subject: message2.subject,
1798
- text: message2.text,
1799
- html: message2.html,
1119
+ from: message.from ?? mailbox.email,
1120
+ to: message.to,
1121
+ cc: message.cc,
1122
+ bcc: message.bcc,
1123
+ subject: message.subject,
1124
+ text: message.text,
1125
+ html: message.html,
1800
1126
  date,
1801
- messageId: id2,
1802
- inReplyTo: message2.inReplyTo,
1803
- references: message2.references,
1804
- attachments: message2.attachments
1127
+ messageId: id,
1128
+ inReplyTo: message.inReplyTo,
1129
+ references: message.references,
1130
+ attachments: message.attachments
1805
1131
  });
1806
1132
  }
1807
1133
 
@@ -1814,7 +1140,7 @@ function batchSemanticMessages(db, mailboxId, messageIds2) {
1814
1140
  WHERE mailbox_id = ? AND id IN (${placeholders})`).all(mailboxId, ...messageIds2);
1815
1141
  if (rows2.length !== messageIds2.length) {
1816
1142
  const found = new Set(rows2.map((row) => row.id));
1817
- const missing = messageIds2.find((id2) => !found.has(id2));
1143
+ const missing = messageIds2.find((id) => !found.has(id));
1818
1144
  if (missing)
1819
1145
  notFound("Message");
1820
1146
  }
@@ -1846,8 +1172,8 @@ function batchSemanticMessages(db, mailboxId, messageIds2) {
1846
1172
  });
1847
1173
  attachmentsByMessage.set(row.message_id, list);
1848
1174
  }
1849
- return messageIds2.map((id2) => {
1850
- const row = byId.get(id2);
1175
+ return messageIds2.map((id) => {
1176
+ const row = byId.get(id);
1851
1177
  return {
1852
1178
  id: row.id,
1853
1179
  threadId: row.thread_id,
@@ -1862,15 +1188,15 @@ function batchSemanticMessages(db, mailboxId, messageIds2) {
1862
1188
  text: row.text_body,
1863
1189
  html: row.html_body,
1864
1190
  sizeEstimate: row.size_estimate,
1865
- labelIds: labelsByMessage.get(id2) ?? [],
1866
- attachments: attachmentsByMessage.get(id2) ?? []
1191
+ labelIds: labelsByMessage.get(id) ?? [],
1192
+ attachments: attachmentsByMessage.get(id) ?? []
1867
1193
  };
1868
1194
  });
1869
1195
  }
1870
1196
  function batchSearchDocuments(db, mailboxId, messages) {
1871
1197
  if (!messages.length)
1872
1198
  return [];
1873
- const ids = messages.map((message2) => message2.id);
1199
+ const ids = messages.map((message) => message.id);
1874
1200
  const placeholders = ids.map(() => "?").join(", ");
1875
1201
  const metaRows = db.prepare(`SELECT id, delivered_to, headers_json FROM messages
1876
1202
  WHERE mailbox_id = ? AND id IN (${placeholders})`).all(mailboxId, ...ids);
@@ -1881,30 +1207,30 @@ function batchSearchDocuments(db, mailboxId, messages) {
1881
1207
  WHERE ml.mailbox_id = ? AND ml.message_id IN (${placeholders}) AND l.type = 'user'
1882
1208
  GROUP BY ml.message_id`).all(mailboxId, ...ids);
1883
1209
  const userLabelCount = new Map(userLabelRows.map((row) => [row.message_id, row.count]));
1884
- return messages.map((message2) => {
1885
- const meta = metaById.get(message2.id);
1210
+ return messages.map((message) => {
1211
+ const meta = metaById.get(message.id);
1886
1212
  return {
1887
- from: message2.from.toLowerCase(),
1888
- to: message2.to.map((item) => item.toLowerCase()),
1889
- cc: message2.cc.map((item) => item.toLowerCase()),
1890
- bcc: message2.bcc.map((item) => item.toLowerCase()),
1213
+ from: message.from.toLowerCase(),
1214
+ to: message.to.map((item) => item.toLowerCase()),
1215
+ cc: message.cc.map((item) => item.toLowerCase()),
1216
+ bcc: message.bcc.map((item) => item.toLowerCase()),
1891
1217
  deliveredTo: meta.delivered_to.toLowerCase(),
1892
- subject: message2.subject,
1893
- text: message2.text,
1894
- html: message2.html,
1895
- dateMs: message2.internalDate,
1896
- rfcMessageId: message2.rfcMessageId,
1897
- size: message2.sizeEstimate,
1898
- labels: message2.labelIds,
1899
- userLabelCount: userLabelCount.get(message2.id) ?? 0,
1900
- attachmentNames: message2.attachments.map((attachment2) => attachment2.filename),
1901
- attachmentMimeTypes: message2.attachments.map((attachment2) => attachment2.mimeType),
1218
+ subject: message.subject,
1219
+ text: message.text,
1220
+ html: message.html,
1221
+ dateMs: message.internalDate,
1222
+ rfcMessageId: message.rfcMessageId,
1223
+ size: message.sizeEstimate,
1224
+ labels: message.labelIds,
1225
+ userLabelCount: userLabelCount.get(message.id) ?? 0,
1226
+ attachmentNames: message.attachments.map((attachment2) => attachment2.filename),
1227
+ attachmentMimeTypes: message.attachments.map((attachment2) => attachment2.mimeType),
1902
1228
  headers: JSON.parse(meta.headers_json)
1903
1229
  };
1904
1230
  });
1905
1231
  }
1906
- function searchDocument(db, mailboxId, message2) {
1907
- return batchSearchDocuments(db, mailboxId, [message2])[0];
1232
+ function searchDocument(db, mailboxId, message) {
1233
+ return batchSearchDocuments(db, mailboxId, [message])[0];
1908
1234
  }
1909
1235
  function searchNow(db) {
1910
1236
  const clock = db.prepare("SELECT value FROM gmail_config WHERE key = 'clock'").get();
@@ -1913,56 +1239,56 @@ function searchNow(db) {
1913
1239
  }
1914
1240
 
1915
1241
  // ../packages/twin-gmail/dist/src/domain/filters.js
1916
- function applyInboundFilters(db, mailboxId, messageId2) {
1242
+ function applyInboundFilters(db, mailboxId, messageId) {
1917
1243
  const filters2 = db.prepare("SELECT criteria_json, action_json FROM filters WHERE mailbox_id = ? ORDER BY id").all(mailboxId);
1918
1244
  for (const row of filters2) {
1919
1245
  const criteria = JSON.parse(row.criteria_json);
1920
1246
  const action = JSON.parse(row.action_json);
1921
1247
  if (action.forward)
1922
1248
  unsupported("Filter forwarding is not implemented");
1923
- const message2 = semanticMessage(db, mailboxId, messageId2);
1924
- const document = searchDocument(db, mailboxId, message2);
1249
+ const message = semanticMessage(db, mailboxId, messageId);
1250
+ const document = searchDocument(db, mailboxId, message);
1925
1251
  const criteriaTo = typeof criteria.to === "string" ? criteria.to.toLowerCase() : void 0;
1926
1252
  const now = searchNow(db);
1927
1253
  const matches = (typeof criteria.from !== "string" || document.from.includes(criteria.from.toLowerCase())) && (criteriaTo === void 0 || document.to.some((to) => to.includes(criteriaTo))) && (typeof criteria.subject !== "string" || document.subject.toLowerCase().includes(criteria.subject.toLowerCase())) && (criteria.hasAttachment !== true || document.attachmentNames.length > 0) && (typeof criteria.size !== "number" || (criteria.sizeComparison === "smaller" ? document.size < criteria.size : document.size > criteria.size)) && (typeof criteria.query !== "string" || matchesSearch(parseSearchQuery(criteria.query), document, now)) && (typeof criteria.negatedQuery !== "string" || !matchesSearch(parseSearchQuery(criteria.negatedQuery), document, now));
1928
1254
  if (!matches)
1929
1255
  continue;
1930
1256
  assertLabels(db, mailboxId, [...action.addLabelIds ?? [], ...action.removeLabelIds ?? []]);
1931
- const beforeLabels = new Set(message2.labelIds);
1257
+ const beforeLabels = new Set(message.labelIds);
1932
1258
  const removed = [];
1933
1259
  const added = [];
1934
1260
  for (const label2 of action.removeLabelIds ?? []) {
1935
- const result = db.prepare("DELETE FROM message_labels WHERE mailbox_id = ? AND message_id = ? AND label_id = ?").run(mailboxId, messageId2, label2);
1261
+ const result = db.prepare("DELETE FROM message_labels WHERE mailbox_id = ? AND message_id = ? AND label_id = ?").run(mailboxId, messageId, label2);
1936
1262
  if (result.changes > 0 && beforeLabels.has(label2))
1937
1263
  removed.push(label2);
1938
1264
  }
1939
1265
  for (const label2 of action.addLabelIds ?? []) {
1940
- const result = db.prepare("INSERT OR IGNORE INTO message_labels(mailbox_id, message_id, label_id) VALUES (?, ?, ?)").run(mailboxId, messageId2, label2);
1266
+ const result = db.prepare("INSERT OR IGNORE INTO message_labels(mailbox_id, message_id, label_id) VALUES (?, ?, ?)").run(mailboxId, messageId, label2);
1941
1267
  if (result.changes > 0 && !beforeLabels.has(label2))
1942
1268
  added.push(label2);
1943
1269
  }
1944
1270
  if (added.length)
1945
- addHistory(db, mailboxId, messageId2, message2.threadId, "labelAdded", added);
1271
+ addHistory(db, mailboxId, messageId, message.threadId, "labelAdded", added);
1946
1272
  if (removed.length)
1947
- addHistory(db, mailboxId, messageId2, message2.threadId, "labelRemoved", removed);
1273
+ addHistory(db, mailboxId, messageId, message.threadId, "labelRemoved", removed);
1948
1274
  }
1949
1275
  }
1950
- function filters(domain, email3) {
1951
- const mailboxId = domain.mailboxId(email3);
1276
+ function filters(domain, email2) {
1277
+ const mailboxId = domain.mailboxId(email2);
1952
1278
  const rows2 = domain.db.prepare("SELECT id, criteria_json, action_json FROM filters WHERE mailbox_id = ? ORDER BY id").all(mailboxId);
1953
1279
  return rows2.map(toFilter);
1954
1280
  }
1955
- function filter(domain, email3, filterId) {
1956
- const mailboxId = domain.mailboxId(email3);
1281
+ function filter(domain, email2, filterId) {
1282
+ const mailboxId = domain.mailboxId(email2);
1957
1283
  const row = domain.db.prepare("SELECT id, criteria_json, action_json FROM filters WHERE mailbox_id = ? AND id = ?").get(mailboxId, filterId);
1958
1284
  if (!row)
1959
1285
  notFound("Filter");
1960
1286
  return toFilter(row);
1961
1287
  }
1962
- function createFilter(domain, email3, criteria = {}, action = {}) {
1288
+ function createFilter(domain, email2, criteria = {}, action = {}) {
1963
1289
  if (action.forward)
1964
1290
  unsupported("Filter action.forward is not supported by the Gmail twin");
1965
- const mailboxId = domain.mailboxId(email3);
1291
+ const mailboxId = domain.mailboxId(email2);
1966
1292
  const count = domain.db.prepare("SELECT COUNT(*) AS count FROM filters WHERE mailbox_id = ?").get(mailboxId);
1967
1293
  if (count.count >= 1e3)
1968
1294
  invalidArgument("Filter limit exceeded");
@@ -1971,12 +1297,12 @@ function createFilter(domain, email3, criteria = {}, action = {}) {
1971
1297
  validateSearchQuery(criteria.query);
1972
1298
  if (criteria.negatedQuery)
1973
1299
  validateSearchQuery(criteria.negatedQuery);
1974
- const id2 = nextId(domain.db, mailboxId, "filter_counter", "filter");
1975
- domain.db.prepare("INSERT INTO filters(mailbox_id, id, criteria_json, action_json) VALUES (?, ?, ?, ?)").run(mailboxId, id2, JSON.stringify(criteria), JSON.stringify(action));
1976
- return { id: id2, criteria, action };
1300
+ const id = nextId(domain.db, mailboxId, "filter_counter", "filter");
1301
+ domain.db.prepare("INSERT INTO filters(mailbox_id, id, criteria_json, action_json) VALUES (?, ?, ?, ?)").run(mailboxId, id, JSON.stringify(criteria), JSON.stringify(action));
1302
+ return { id, criteria, action };
1977
1303
  }
1978
- function deleteFilter(domain, email3, filterId) {
1979
- const mailboxId = domain.mailboxId(email3);
1304
+ function deleteFilter(domain, email2, filterId) {
1305
+ const mailboxId = domain.mailboxId(email2);
1980
1306
  const result = domain.db.prepare("DELETE FROM filters WHERE mailbox_id = ? AND id = ?").run(mailboxId, filterId);
1981
1307
  if (result.changes === 0)
1982
1308
  notFound("Filter");
@@ -2159,47 +1485,47 @@ function pushNotHasLabel(clauses, params, labelId) {
2159
1485
  }
2160
1486
 
2161
1487
  // ../packages/twin-gmail/dist/src/domain/messages.js
2162
- function getRaw(domain, email3, messageId2) {
2163
- return rawMessage(domain.db, domain.mailboxId(email3), messageId2);
1488
+ function getRaw(domain, email2, messageId) {
1489
+ return rawMessage(domain.db, domain.mailboxId(email2), messageId);
2164
1490
  }
2165
- function getMessage(domain, email3, messageId2) {
2166
- return semanticMessage(domain.db, domain.mailboxId(email3), messageId2);
1491
+ function getMessage(domain, email2, messageId) {
1492
+ return semanticMessage(domain.db, domain.mailboxId(email2), messageId);
2167
1493
  }
2168
- function getThread(domain, email3, threadId) {
2169
- const mailboxId = domain.mailboxId(email3);
1494
+ function getThread(domain, email2, threadId) {
1495
+ const mailboxId = domain.mailboxId(email2);
2170
1496
  const rows2 = domain.db.prepare("SELECT id FROM messages WHERE mailbox_id = ? AND thread_id = ? ORDER BY internal_date, id").all(mailboxId, threadId);
2171
1497
  if (!rows2.length)
2172
1498
  notFound("Thread");
2173
1499
  const messages = batchSemanticMessages(domain.db, mailboxId, rows2.map((row) => row.id));
2174
1500
  return {
2175
1501
  id: threadId,
2176
- labelIds: [...new Set(messages.flatMap((message2) => message2.labelIds))].sort(),
1502
+ labelIds: [...new Set(messages.flatMap((message) => message.labelIds))].sort(),
2177
1503
  messages
2178
1504
  };
2179
1505
  }
2180
- function insertMessage(domain, email3, raw, options = {}) {
2181
- const mailboxId = domain.mailboxId(email3);
1506
+ function insertMessage(domain, email2, raw, options = {}) {
1507
+ const mailboxId = domain.mailboxId(email2);
2182
1508
  const bytes = acceptedRaw(raw);
2183
1509
  return domain.db.transaction(() => {
2184
- const message2 = insertStoredMessage(domain.db, mailboxId, bytes, {
1510
+ const message = insertStoredMessage(domain.db, mailboxId, bytes, {
2185
1511
  threadId: options.threadId,
2186
1512
  labels: options.labels ?? (options.incoming ? ["INBOX"] : []),
2187
- deliveredTo: options.incoming ? email3 : void 0
1513
+ deliveredTo: options.incoming ? email2 : void 0
2188
1514
  });
2189
1515
  if (options.incoming)
2190
- applyInboundFilters(domain.db, mailboxId, message2.id);
2191
- return semanticMessage(domain.db, mailboxId, message2.id);
1516
+ applyInboundFilters(domain.db, mailboxId, message.id);
1517
+ return semanticMessage(domain.db, mailboxId, message.id);
2192
1518
  }).immediate();
2193
1519
  }
2194
- function sendMessage(domain, email3, raw, options = {}) {
1520
+ function sendMessage(domain, email2, raw, options = {}) {
2195
1521
  checkFault(domain.db, "messages.send");
2196
- const senderMailboxId = domain.mailboxId(email3);
1522
+ const senderMailboxId = domain.mailboxId(email2);
2197
1523
  const bytes = acceptedRaw(raw);
2198
1524
  const parsed = parseMime(bytes);
2199
1525
  assertAcceptedFrom(domain, senderMailboxId, parsed.from);
2200
1526
  return domain.db.transaction(() => {
2201
1527
  const recipientEmails = [...new Set([...parsed.to, ...parsed.cc, ...parsed.bcc].map((item) => item.toLowerCase()))];
2202
- const selfDelivery = deliveryMode(domain) === "seeded-mailboxes" && recipientEmails.includes(email3.toLowerCase());
1528
+ const selfDelivery = deliveryMode(domain) === "seeded-mailboxes" && recipientEmails.includes(email2.toLowerCase());
2203
1529
  const sender = insertStoredMessage(domain.db, senderMailboxId, bytes, {
2204
1530
  threadId: options.threadId,
2205
1531
  labels: selfDelivery ? ["INBOX", "SENT"] : ["SENT"]
@@ -2210,7 +1536,7 @@ function sendMessage(domain, email3, raw, options = {}) {
2210
1536
  if (deliveryMode(domain) === "seeded-mailboxes") {
2211
1537
  const visibleRaw = stripBcc(bytes);
2212
1538
  for (const recipient of recipientEmails) {
2213
- if (recipient === email3.toLowerCase())
1539
+ if (recipient === email2.toLowerCase())
2214
1540
  continue;
2215
1541
  const mailbox = domain.db.prepare("SELECT id, email FROM mailboxes WHERE email = ? COLLATE NOCASE").get(recipient);
2216
1542
  if (!mailbox)
@@ -2226,11 +1552,11 @@ function sendMessage(domain, email3, raw, options = {}) {
2226
1552
  return { sender: semanticMessage(domain.db, senderMailboxId, sender.id), deliveries };
2227
1553
  }).immediate();
2228
1554
  }
2229
- function modifyMessageLabels(domain, email3, messageId2, add = [], remove = []) {
2230
- const mailboxId = domain.mailboxId(email3);
1555
+ function modifyMessageLabels(domain, email2, messageId, add = [], remove = []) {
1556
+ const mailboxId = domain.mailboxId(email2);
2231
1557
  return domain.db.transaction(() => {
2232
- const before = semanticMessage(domain.db, mailboxId, messageId2);
2233
- if (before.labelIds.includes("DRAFT") && (add.some((id2) => id2 !== "DRAFT") || remove.includes("DRAFT"))) {
1558
+ const before = semanticMessage(domain.db, mailboxId, messageId);
1559
+ if (before.labelIds.includes("DRAFT") && (add.some((id) => id !== "DRAFT") || remove.includes("DRAFT"))) {
2234
1560
  invalidArgument("Draft messages may only carry the DRAFT label");
2235
1561
  }
2236
1562
  if (!before.labelIds.includes("DRAFT") && add.includes("DRAFT")) {
@@ -2246,87 +1572,87 @@ function modifyMessageLabels(domain, email3, messageId2, add = [], remove = [])
2246
1572
  const actualRemove = [...removeSet].filter((label2) => before.labelIds.includes(label2));
2247
1573
  const actualAdd = [...addSet].filter((label2) => !before.labelIds.includes(label2));
2248
1574
  for (const label2 of actualRemove) {
2249
- domain.db.prepare("DELETE FROM message_labels WHERE mailbox_id = ? AND message_id = ? AND label_id = ?").run(mailboxId, messageId2, label2);
1575
+ domain.db.prepare("DELETE FROM message_labels WHERE mailbox_id = ? AND message_id = ? AND label_id = ?").run(mailboxId, messageId, label2);
2250
1576
  }
2251
1577
  for (const label2 of actualAdd) {
2252
- domain.db.prepare("INSERT OR IGNORE INTO message_labels(mailbox_id, message_id, label_id) VALUES (?, ?, ?)").run(mailboxId, messageId2, label2);
1578
+ domain.db.prepare("INSERT OR IGNORE INTO message_labels(mailbox_id, message_id, label_id) VALUES (?, ?, ?)").run(mailboxId, messageId, label2);
2253
1579
  }
2254
1580
  if (actualAdd.length)
2255
- addHistory(domain.db, mailboxId, messageId2, before.threadId, "labelAdded", actualAdd);
1581
+ addHistory(domain.db, mailboxId, messageId, before.threadId, "labelAdded", actualAdd);
2256
1582
  if (actualRemove.length) {
2257
- addHistory(domain.db, mailboxId, messageId2, before.threadId, "labelRemoved", actualRemove);
1583
+ addHistory(domain.db, mailboxId, messageId, before.threadId, "labelRemoved", actualRemove);
2258
1584
  }
2259
- return semanticMessage(domain.db, mailboxId, messageId2);
1585
+ return semanticMessage(domain.db, mailboxId, messageId);
2260
1586
  }).immediate();
2261
1587
  }
2262
- function modifyThreadLabels(domain, email3, threadId, add = [], remove = []) {
1588
+ function modifyThreadLabels(domain, email2, threadId, add = [], remove = []) {
2263
1589
  return domain.db.transaction(() => {
2264
- const thread = getThread(domain, email3, threadId);
2265
- for (const message2 of thread.messages)
2266
- modifyMessageLabels(domain, email3, message2.id, add, remove);
2267
- return getThread(domain, email3, threadId);
1590
+ const thread = getThread(domain, email2, threadId);
1591
+ for (const message of thread.messages)
1592
+ modifyMessageLabels(domain, email2, message.id, add, remove);
1593
+ return getThread(domain, email2, threadId);
2268
1594
  }).immediate();
2269
1595
  }
2270
- function deleteMessage(domain, email3, messageId2) {
2271
- const mailboxId = domain.mailboxId(email3);
1596
+ function deleteMessage(domain, email2, messageId) {
1597
+ const mailboxId = domain.mailboxId(email2);
2272
1598
  domain.db.transaction(() => {
2273
- const message2 = semanticMessage(domain.db, mailboxId, messageId2);
2274
- domain.db.prepare("DELETE FROM messages WHERE mailbox_id = ? AND id = ?").run(mailboxId, messageId2);
2275
- addHistory(domain.db, mailboxId, messageId2, message2.threadId, "messageDeleted");
2276
- removeEmptyThread(domain, mailboxId, message2.threadId);
1599
+ const message = semanticMessage(domain.db, mailboxId, messageId);
1600
+ domain.db.prepare("DELETE FROM messages WHERE mailbox_id = ? AND id = ?").run(mailboxId, messageId);
1601
+ addHistory(domain.db, mailboxId, messageId, message.threadId, "messageDeleted");
1602
+ removeEmptyThread(domain, mailboxId, message.threadId);
2277
1603
  }).immediate();
2278
1604
  }
2279
- function batchDeleteMessages(domain, email3, messageIds2) {
2280
- const mailboxId = domain.mailboxId(email3);
1605
+ function batchDeleteMessages(domain, email2, messageIds2) {
1606
+ const mailboxId = domain.mailboxId(email2);
2281
1607
  domain.db.transaction(() => {
2282
- for (const messageId2 of messageIds2) {
2283
- const exists = domain.db.prepare("SELECT thread_id FROM messages WHERE mailbox_id = ? AND id = ?").get(mailboxId, messageId2);
1608
+ for (const messageId of messageIds2) {
1609
+ const exists = domain.db.prepare("SELECT thread_id FROM messages WHERE mailbox_id = ? AND id = ?").get(mailboxId, messageId);
2284
1610
  if (!exists)
2285
1611
  continue;
2286
- domain.db.prepare("DELETE FROM messages WHERE mailbox_id = ? AND id = ?").run(mailboxId, messageId2);
2287
- addHistory(domain.db, mailboxId, messageId2, exists.thread_id, "messageDeleted");
1612
+ domain.db.prepare("DELETE FROM messages WHERE mailbox_id = ? AND id = ?").run(mailboxId, messageId);
1613
+ addHistory(domain.db, mailboxId, messageId, exists.thread_id, "messageDeleted");
2288
1614
  removeEmptyThread(domain, mailboxId, exists.thread_id);
2289
1615
  }
2290
1616
  }).immediate();
2291
1617
  }
2292
- function deleteThread(domain, email3, threadId) {
2293
- const mailboxId = domain.mailboxId(email3);
1618
+ function deleteThread(domain, email2, threadId) {
1619
+ const mailboxId = domain.mailboxId(email2);
2294
1620
  domain.db.transaction(() => {
2295
- const thread = getThread(domain, email3, threadId);
1621
+ const thread = getThread(domain, email2, threadId);
2296
1622
  domain.db.prepare("DELETE FROM threads WHERE mailbox_id = ? AND id = ?").run(mailboxId, threadId);
2297
- for (const message2 of thread.messages) {
2298
- addHistory(domain.db, mailboxId, message2.id, threadId, "messageDeleted");
1623
+ for (const message of thread.messages) {
1624
+ addHistory(domain.db, mailboxId, message.id, threadId, "messageDeleted");
2299
1625
  }
2300
1626
  }).immediate();
2301
1627
  }
2302
- function headers(domain, email3, messageId2) {
2303
- const mailboxId = domain.mailboxId(email3);
2304
- const row = domain.db.prepare("SELECT headers_json FROM messages WHERE mailbox_id = ? AND id = ?").get(mailboxId, messageId2);
1628
+ function headers(domain, email2, messageId) {
1629
+ const mailboxId = domain.mailboxId(email2);
1630
+ const row = domain.db.prepare("SELECT headers_json FROM messages WHERE mailbox_id = ? AND id = ?").get(mailboxId, messageId);
2305
1631
  if (!row)
2306
1632
  notFound("Message");
2307
1633
  return JSON.parse(row.headers_json);
2308
1634
  }
2309
- function attachment(domain, email3, messageId2, attachmentId) {
2310
- const mailboxId = domain.mailboxId(email3);
2311
- const row = domain.db.prepare("SELECT size, data FROM attachments WHERE mailbox_id = ? AND message_id = ? AND id = ?").get(mailboxId, messageId2, attachmentId);
1635
+ function attachment(domain, email2, messageId, attachmentId) {
1636
+ const mailboxId = domain.mailboxId(email2);
1637
+ const row = domain.db.prepare("SELECT size, data FROM attachments WHERE mailbox_id = ? AND message_id = ? AND id = ?").get(mailboxId, messageId, attachmentId);
2312
1638
  if (!row)
2313
1639
  notFound("Attachment");
2314
1640
  return { size: row.size, data: Buffer.from(row.data).toString("base64url") };
2315
1641
  }
2316
- function applyInternalDateSource(domain, email3, messageId2, source) {
2317
- const mailboxId = domain.mailboxId(email3);
1642
+ function applyInternalDateSource(domain, email2, messageId, source) {
1643
+ const mailboxId = domain.mailboxId(email2);
2318
1644
  if (source === "receivedTime") {
2319
1645
  const history = domain.db.prepare(`SELECT created_at FROM history
2320
1646
  WHERE mailbox_id = ? AND message_id = ? AND event_type = 'messageAdded'
2321
- ORDER BY id DESC LIMIT 1`).get(mailboxId, messageId2);
1647
+ ORDER BY id DESC LIMIT 1`).get(mailboxId, messageId);
2322
1648
  if (history) {
2323
- domain.db.prepare("UPDATE messages SET internal_date = ? WHERE mailbox_id = ? AND id = ?").run(Date.parse(history.created_at), mailboxId, messageId2);
1649
+ domain.db.prepare("UPDATE messages SET internal_date = ? WHERE mailbox_id = ? AND id = ?").run(Date.parse(history.created_at), mailboxId, messageId);
2324
1650
  }
2325
1651
  }
2326
- return semanticMessage(domain.db, mailboxId, messageId2);
1652
+ return semanticMessage(domain.db, mailboxId, messageId);
2327
1653
  }
2328
- function searchMessages(domain, email3, query = "", options = {}) {
2329
- const mailboxId = domain.mailboxId(email3);
1654
+ function searchMessages(domain, email2, query = "", options = {}) {
1655
+ const mailboxId = domain.mailboxId(email2);
2330
1656
  const ast = validateSearchQuery(query);
2331
1657
  const explicitAnywhere = /\bin:(anywhere|trash|spam|draft)\b/i.test(query);
2332
1658
  const plan = compileSearchToSql(ast, {
@@ -2356,16 +1682,16 @@ function searchMessages(domain, email3, query = "", options = {}) {
2356
1682
  return messages;
2357
1683
  const now = searchNow(domain.db);
2358
1684
  const documents = batchSearchDocuments(domain.db, mailboxId, messages);
2359
- return messages.filter((message2, index) => {
2360
- if (!explicitAnywhere && !options.includeTrash && message2.labelIds.some((id2) => ["TRASH", "SPAM", "DRAFT"].includes(id2))) {
1685
+ return messages.filter((message, index) => {
1686
+ if (!explicitAnywhere && !options.includeTrash && message.labelIds.some((id) => ["TRASH", "SPAM", "DRAFT"].includes(id))) {
2361
1687
  return false;
2362
1688
  }
2363
1689
  return matchesSearch(ast, documents[index], now);
2364
1690
  });
2365
1691
  }
2366
- function searchThreads(domain, email3, query = "", options = {}) {
2367
- const matches = searchMessages(domain, email3, query, options);
2368
- return [...new Set(matches.map((message2) => message2.threadId))].map((threadId) => getThread(domain, email3, threadId));
1692
+ function searchThreads(domain, email2, query = "", options = {}) {
1693
+ const matches = searchMessages(domain, email2, query, options);
1694
+ return [...new Set(matches.map((message) => message.threadId))].map((threadId) => getThread(domain, email2, threadId));
2369
1695
  }
2370
1696
  function removeEmptyThread(domain, mailboxId, threadId) {
2371
1697
  const member = domain.db.prepare("SELECT 1 FROM messages WHERE mailbox_id = ? AND thread_id = ? LIMIT 1").get(mailboxId, threadId);
@@ -2390,27 +1716,27 @@ function assertAcceptedFrom(domain, mailboxId, from) {
2390
1716
  }
2391
1717
 
2392
1718
  // ../packages/twin-gmail/dist/src/domain/drafts.js
2393
- function createDraft(domain, email3, raw, options = {}) {
2394
- const mailboxId = domain.mailboxId(email3);
1719
+ function createDraft(domain, email2, raw, options = {}) {
1720
+ const mailboxId = domain.mailboxId(email2);
2395
1721
  const bytes = acceptedRaw(raw);
2396
1722
  return domain.db.transaction(() => {
2397
- const message2 = insertStoredMessage(domain.db, mailboxId, bytes, {
1723
+ const message = insertStoredMessage(domain.db, mailboxId, bytes, {
2398
1724
  threadId: options.threadId,
2399
1725
  draft: true
2400
1726
  });
2401
1727
  const draftId = nextId(domain.db, mailboxId, "draft_counter", "draft");
2402
1728
  const now = nextTimestamp(domain.db, mailboxId);
2403
- domain.db.prepare("INSERT INTO drafts(mailbox_id, id, message_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(mailboxId, draftId, message2.id, now, now);
2404
- addHistory(domain.db, mailboxId, message2.id, message2.threadId, "draftCreated", ["DRAFT"]);
2405
- return { id: draftId, message: message2 };
1729
+ domain.db.prepare("INSERT INTO drafts(mailbox_id, id, message_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(mailboxId, draftId, message.id, now, now);
1730
+ addHistory(domain.db, mailboxId, message.id, message.threadId, "draftCreated", ["DRAFT"]);
1731
+ return { id: draftId, message };
2406
1732
  }).immediate();
2407
1733
  }
2408
- function createComposedDraft(domain, email3, input) {
2409
- const mailboxId = domain.mailboxId(email3);
1734
+ function createComposedDraft(domain, email2, input) {
1735
+ const mailboxId = domain.mailboxId(email2);
2410
1736
  return domain.db.transaction(() => {
2411
1737
  const reply = input.replyToMessageId ? semanticMessage(domain.db, mailboxId, input.replyToMessageId) : void 0;
2412
1738
  const raw = composeMime({
2413
- from: email3,
1739
+ from: email2,
2414
1740
  to: input.to,
2415
1741
  cc: input.cc,
2416
1742
  bcc: input.bcc,
@@ -2423,20 +1749,20 @@ function createComposedDraft(domain, email3, input) {
2423
1749
  references: reply ? [reply.rfcMessageId] : void 0,
2424
1750
  attachments: input.attachments
2425
1751
  });
2426
- return createDraft(domain, email3, raw, { threadId: reply?.threadId });
1752
+ return createDraft(domain, email2, raw, { threadId: reply?.threadId });
2427
1753
  }).immediate();
2428
1754
  }
2429
- function listDrafts(domain, email3, query = "") {
2430
- const mailboxId = domain.mailboxId(email3);
1755
+ function listDrafts(domain, email2, query = "") {
1756
+ const mailboxId = domain.mailboxId(email2);
2431
1757
  const rows2 = domain.db.prepare(`SELECT d.id, d.message_id
2432
1758
  FROM drafts d JOIN messages m ON m.mailbox_id = d.mailbox_id AND m.id = d.message_id
2433
1759
  WHERE d.mailbox_id = ? ORDER BY d.updated_at DESC, d.id DESC`).all(mailboxId);
2434
- const matchingIds = query ? new Set(searchMessages(domain, email3, `in:draft ${query}`, { includeTrash: true }).map((message2) => message2.id)) : void 0;
1760
+ const matchingIds = query ? new Set(searchMessages(domain, email2, `in:draft ${query}`, { includeTrash: true }).map((message) => message.id)) : void 0;
2435
1761
  return rows2.filter((row) => matchingIds?.has(row.message_id) ?? true).map((row) => ({ id: row.id, message: semanticMessage(domain.db, mailboxId, row.message_id) }));
2436
1762
  }
2437
- function drafts(domain, email3, query = "", includeTrash = false) {
2438
- const mailboxId = domain.mailboxId(email3);
2439
- const matching = query ? new Set(searchMessages(domain, email3, query, { includeTrash }).map((message2) => message2.id)) : null;
1763
+ function drafts(domain, email2, query = "", includeTrash = false) {
1764
+ const mailboxId = domain.mailboxId(email2);
1765
+ const matching = query ? new Set(searchMessages(domain, email2, query, { includeTrash }).map((message) => message.id)) : null;
2440
1766
  const rows2 = domain.db.prepare("SELECT id, message_id, updated_at FROM drafts WHERE mailbox_id = ? ORDER BY updated_at DESC, id DESC").all(mailboxId);
2441
1767
  return rows2.filter((row) => !matching || matching.has(row.message_id)).map((row) => ({
2442
1768
  id: row.id,
@@ -2444,15 +1770,15 @@ function drafts(domain, email3, query = "", includeTrash = false) {
2444
1770
  updatedAt: row.updated_at
2445
1771
  }));
2446
1772
  }
2447
- function draft(domain, email3, draftId) {
2448
- const mailboxId = domain.mailboxId(email3);
1773
+ function draft(domain, email2, draftId) {
1774
+ const mailboxId = domain.mailboxId(email2);
2449
1775
  const row = domain.db.prepare("SELECT id, message_id, updated_at FROM drafts WHERE mailbox_id = ? AND id = ?").get(mailboxId, draftId);
2450
1776
  if (!row)
2451
1777
  notFound("Draft");
2452
1778
  return { id: row.id, message: semanticMessage(domain.db, mailboxId, row.message_id), updatedAt: row.updated_at };
2453
1779
  }
2454
- function updateDraft(domain, email3, draftId, raw, options = {}) {
2455
- const mailboxId = domain.mailboxId(email3);
1780
+ function updateDraft(domain, email2, draftId, raw, options = {}) {
1781
+ const mailboxId = domain.mailboxId(email2);
2456
1782
  const bytes = acceptedRaw(raw);
2457
1783
  return domain.db.transaction(() => {
2458
1784
  const draftRow = requireDraft(domain, mailboxId, draftId);
@@ -2469,8 +1795,8 @@ function updateDraft(domain, email3, draftId, raw, options = {}) {
2469
1795
  return { id: draftId, message: replacement };
2470
1796
  }).immediate();
2471
1797
  }
2472
- function sendDraft(domain, email3, draftId) {
2473
- const mailboxId = domain.mailboxId(email3);
1798
+ function sendDraft(domain, email2, draftId) {
1799
+ const mailboxId = domain.mailboxId(email2);
2474
1800
  return domain.db.transaction(() => {
2475
1801
  const draftRow = requireDraft(domain, mailboxId, draftId);
2476
1802
  const raw = rawMessage(domain.db, mailboxId, draftRow.message_id);
@@ -2478,17 +1804,17 @@ function sendDraft(domain, email3, draftId) {
2478
1804
  domain.db.prepare("DELETE FROM drafts WHERE mailbox_id = ? AND id = ?").run(mailboxId, draftId);
2479
1805
  domain.db.prepare("DELETE FROM messages WHERE mailbox_id = ? AND id = ?").run(mailboxId, draftRow.message_id);
2480
1806
  addHistory(domain.db, mailboxId, draftRow.message_id, old.threadId, "draftSent");
2481
- return sendMessage(domain, email3, raw, { threadId: old.threadId });
1807
+ return sendMessage(domain, email2, raw, { threadId: old.threadId });
2482
1808
  }).immediate();
2483
1809
  }
2484
- function deleteDraft(domain, email3, draftId) {
2485
- const mailboxId = domain.mailboxId(email3);
1810
+ function deleteDraft(domain, email2, draftId) {
1811
+ const mailboxId = domain.mailboxId(email2);
2486
1812
  domain.db.transaction(() => {
2487
1813
  const draftRow = requireDraft(domain, mailboxId, draftId);
2488
- const message2 = semanticMessage(domain.db, mailboxId, draftRow.message_id);
1814
+ const message = semanticMessage(domain.db, mailboxId, draftRow.message_id);
2489
1815
  domain.db.prepare("DELETE FROM messages WHERE mailbox_id = ? AND id = ?").run(mailboxId, draftRow.message_id);
2490
- removeEmptyThread(domain, mailboxId, message2.threadId);
2491
- addHistory(domain.db, mailboxId, draftRow.message_id, message2.threadId, "draftDeleted");
1816
+ removeEmptyThread(domain, mailboxId, message.threadId);
1817
+ addHistory(domain.db, mailboxId, draftRow.message_id, message.threadId, "draftDeleted");
2492
1818
  }).immediate();
2493
1819
  }
2494
1820
  function requireDraft(domain, mailboxId, draftId) {
@@ -2499,22 +1825,22 @@ function requireDraft(domain, mailboxId, draftId) {
2499
1825
  }
2500
1826
 
2501
1827
  // ../packages/twin-gmail/dist/src/domain/labels.js
2502
- function createLabel(domain, email3, name, color) {
2503
- const mailboxId = domain.mailboxId(email3);
1828
+ function createLabel(domain, email2, name, color) {
1829
+ const mailboxId = domain.mailboxId(email2);
2504
1830
  if (!name.trim())
2505
1831
  invalidArgument("Label name is required");
2506
1832
  return domain.db.transaction(() => {
2507
- const id2 = nextId(domain.db, mailboxId, "label_counter", "Label");
1833
+ const id = nextId(domain.db, mailboxId, "label_counter", "Label");
2508
1834
  try {
2509
- domain.db.prepare("INSERT INTO labels(mailbox_id, id, name, type, text_color, background_color) VALUES (?, ?, ?, 'user', ?, ?)").run(mailboxId, id2, name.trim(), color?.textColor ?? null, color?.backgroundColor ?? null);
1835
+ domain.db.prepare("INSERT INTO labels(mailbox_id, id, name, type, text_color, background_color) VALUES (?, ?, ?, 'user', ?, ?)").run(mailboxId, id, name.trim(), color?.textColor ?? null, color?.backgroundColor ?? null);
2510
1836
  } catch {
2511
1837
  invalidArgument(`Label already exists: ${name}`);
2512
1838
  }
2513
- return { id: id2, name: name.trim() };
1839
+ return { id, name: name.trim() };
2514
1840
  }).immediate();
2515
1841
  }
2516
- function listUserLabels(domain, email3) {
2517
- const mailboxId = domain.mailboxId(email3);
1842
+ function listUserLabels(domain, email2) {
1843
+ const mailboxId = domain.mailboxId(email2);
2518
1844
  const rows2 = domain.db.prepare(`SELECT l.id, l.name, l.text_color, l.background_color,
2519
1845
  COUNT(DISTINCT m.thread_id) AS threads_total,
2520
1846
  COUNT(DISTINCT CASE WHEN unread.message_id IS NOT NULL THEN m.thread_id END) AS threads_unread
@@ -2543,8 +1869,8 @@ function listUserLabels(domain, email3) {
2543
1869
  threadsUnread: row.threads_unread
2544
1870
  }));
2545
1871
  }
2546
- function labels(domain, email3) {
2547
- const mailboxId = domain.mailboxId(email3);
1872
+ function labels(domain, email2) {
1873
+ const mailboxId = domain.mailboxId(email2);
2548
1874
  const rows2 = domain.db.prepare(`SELECT l.id, l.name, l.type, l.text_color, l.background_color,
2549
1875
  COUNT(DISTINCT ml.message_id) AS messagesTotal,
2550
1876
  COUNT(DISTINCT CASE WHEN unread.message_id IS NOT NULL THEN ml.message_id END) AS messagesUnread,
@@ -2574,15 +1900,15 @@ function labels(domain, email3) {
2574
1900
  threadsUnread: row.threadsUnread
2575
1901
  }));
2576
1902
  }
2577
- function label(domain, email3, labelId) {
2578
- const found = labels(domain, email3).find((item) => item.id === labelId);
1903
+ function label(domain, email2, labelId) {
1904
+ const found = labels(domain, email2).find((item) => item.id === labelId);
2579
1905
  if (!found)
2580
1906
  notFound("Label");
2581
1907
  return found;
2582
1908
  }
2583
- function updateLabel(domain, email3, labelId, input, replace) {
2584
- const mailboxId = domain.mailboxId(email3);
2585
- const current = label(domain, email3, labelId);
1909
+ function updateLabel(domain, email2, labelId, input, replace) {
1910
+ const mailboxId = domain.mailboxId(email2);
1911
+ const current = label(domain, email2, labelId);
2586
1912
  if (current.type !== "user")
2587
1913
  invalidArgument("System labels cannot be modified");
2588
1914
  const name = replace ? input.name : input.name ?? current.name;
@@ -2595,38 +1921,38 @@ function updateLabel(domain, email3, labelId, input, replace) {
2595
1921
  } catch {
2596
1922
  invalidArgument(`Label already exists: ${name}`);
2597
1923
  }
2598
- return label(domain, email3, labelId);
1924
+ return label(domain, email2, labelId);
2599
1925
  }
2600
- function deleteLabel(domain, email3, labelId) {
2601
- const mailboxId = domain.mailboxId(email3);
2602
- const current = label(domain, email3, labelId);
1926
+ function deleteLabel(domain, email2, labelId) {
1927
+ const mailboxId = domain.mailboxId(email2);
1928
+ const current = label(domain, email2, labelId);
2603
1929
  if (current.type !== "user")
2604
1930
  invalidArgument("System labels cannot be deleted");
2605
1931
  domain.db.prepare("DELETE FROM labels WHERE mailbox_id = ? AND id = ?").run(mailboxId, labelId);
2606
1932
  }
2607
1933
 
2608
1934
  // ../packages/twin-gmail/dist/src/domain/settings.js
2609
- function profile(domain, email3) {
2610
- const mailboxId = domain.mailboxId(email3);
1935
+ function profile(domain, email2) {
1936
+ const mailboxId = domain.mailboxId(email2);
2611
1937
  const totals = domain.db.prepare(`SELECT COUNT(*) AS messagesTotal, COUNT(DISTINCT thread_id) AS threadsTotal
2612
1938
  FROM messages WHERE mailbox_id = ?`).get(mailboxId);
2613
- return { emailAddress: email3, ...totals, historyId: currentHistoryId(domain, mailboxId) };
1939
+ return { emailAddress: email2, ...totals, historyId: currentHistoryId(domain, mailboxId) };
2614
1940
  }
2615
- function currentHistoryIdFor(domain, email3) {
2616
- return currentHistoryId(domain, domain.mailboxId(email3));
1941
+ function currentHistoryIdFor(domain, email2) {
1942
+ return currentHistoryId(domain, domain.mailboxId(email2));
2617
1943
  }
2618
- function latestMessageHistory(domain, email3, messageId2) {
2619
- const mailboxId = domain.mailboxId(email3);
2620
- const row = domain.db.prepare("SELECT MAX(id) AS id FROM history WHERE mailbox_id = ? AND message_id = ?").get(mailboxId, messageId2);
1944
+ function latestMessageHistory(domain, email2, messageId) {
1945
+ const mailboxId = domain.mailboxId(email2);
1946
+ const row = domain.db.prepare("SELECT MAX(id) AS id FROM history WHERE mailbox_id = ? AND message_id = ?").get(mailboxId, messageId);
2621
1947
  return String(row.id ?? Number(currentHistoryId(domain, mailboxId)));
2622
1948
  }
2623
- function latestThreadHistory(domain, email3, threadId) {
2624
- const mailboxId = domain.mailboxId(email3);
1949
+ function latestThreadHistory(domain, email2, threadId) {
1950
+ const mailboxId = domain.mailboxId(email2);
2625
1951
  const row = domain.db.prepare("SELECT MAX(id) AS id FROM history WHERE mailbox_id = ? AND thread_id = ?").get(mailboxId, threadId);
2626
1952
  return String(row.id ?? Number(currentHistoryId(domain, mailboxId)));
2627
1953
  }
2628
- function listHistory(domain, email3, startHistoryId, options = {}) {
2629
- const mailboxId = domain.mailboxId(email3);
1954
+ function listHistory(domain, email2, startHistoryId, options = {}) {
1955
+ const mailboxId = domain.mailboxId(email2);
2630
1956
  const start = Number(startHistoryId);
2631
1957
  if (!Number.isSafeInteger(start) || start < 0)
2632
1958
  invalidArgument("Invalid startHistoryId");
@@ -2649,19 +1975,19 @@ function listHistory(domain, email3, startHistoryId, options = {}) {
2649
1975
  historyId: String(current)
2650
1976
  };
2651
1977
  }
2652
- function forwardingAddresses(domain, email3) {
2653
- const mailboxId = domain.mailboxId(email3);
1978
+ function forwardingAddresses(domain, email2) {
1979
+ const mailboxId = domain.mailboxId(email2);
2654
1980
  const rows2 = domain.db.prepare("SELECT email, verification_status FROM forwarding_addresses WHERE mailbox_id = ? ORDER BY email COLLATE NOCASE").all(mailboxId);
2655
1981
  return rows2.map((row) => ({ forwardingEmail: row.email, verificationStatus: row.verification_status }));
2656
1982
  }
2657
- function forwardingAddress(domain, email3, forwardingEmail) {
2658
- const found = forwardingAddresses(domain, email3).find((item) => item.forwardingEmail.toLowerCase() === forwardingEmail.toLowerCase());
1983
+ function forwardingAddress(domain, email2, forwardingEmail) {
1984
+ const found = forwardingAddresses(domain, email2).find((item) => item.forwardingEmail.toLowerCase() === forwardingEmail.toLowerCase());
2659
1985
  if (!found)
2660
1986
  notFound("Forwarding address");
2661
1987
  return found;
2662
1988
  }
2663
- function sendAs(domain, email3) {
2664
- const mailboxId = domain.mailboxId(email3);
1989
+ function sendAs(domain, email2) {
1990
+ const mailboxId = domain.mailboxId(email2);
2665
1991
  const rows2 = domain.db.prepare(`SELECT email, display_name, reply_to_address, is_primary, is_default, verification_status
2666
1992
  FROM send_as WHERE mailbox_id = ? ORDER BY is_primary DESC, email COLLATE NOCASE`).all(mailboxId);
2667
1993
  return rows2.map((row) => ({
@@ -2675,8 +2001,8 @@ function sendAs(domain, email3) {
2675
2001
  signature: ""
2676
2002
  }));
2677
2003
  }
2678
- function sendAsAddress(domain, email3, sendAsEmail) {
2679
- const found = sendAs(domain, email3).find((item) => String(item.sendAsEmail).toLowerCase() === sendAsEmail.toLowerCase());
2004
+ function sendAsAddress(domain, email2, sendAsEmail) {
2005
+ const found = sendAs(domain, email2).find((item) => String(item.sendAsEmail).toLowerCase() === sendAsEmail.toLowerCase());
2680
2006
  if (!found)
2681
2007
  notFound("Send-as alias");
2682
2008
  return found;
@@ -2711,730 +2037,143 @@ var GmailDomain = class {
2711
2037
  this.seed(defaultSeedState());
2712
2038
  return { ok: true };
2713
2039
  }
2714
- mailboxId(email3) {
2715
- const row = this.db.prepare("SELECT id FROM mailboxes WHERE email = ? COLLATE NOCASE").get(email3);
2040
+ mailboxId(email2) {
2041
+ const row = this.db.prepare("SELECT id FROM mailboxes WHERE email = ? COLLATE NOCASE").get(email2);
2716
2042
  if (!row)
2717
2043
  notFound("User");
2718
2044
  return row.id;
2719
2045
  }
2720
- getRaw(email3, messageId2) {
2721
- return getRaw(this, email3, messageId2);
2046
+ getRaw(email2, messageId) {
2047
+ return getRaw(this, email2, messageId);
2722
2048
  }
2723
- getMessage(email3, messageId2) {
2724
- return getMessage(this, email3, messageId2);
2049
+ getMessage(email2, messageId) {
2050
+ return getMessage(this, email2, messageId);
2725
2051
  }
2726
- getThread(email3, threadId) {
2727
- return getThread(this, email3, threadId);
2052
+ getThread(email2, threadId) {
2053
+ return getThread(this, email2, threadId);
2728
2054
  }
2729
- insertMessage(email3, raw, options = {}) {
2730
- return insertMessage(this, email3, raw, options);
2055
+ insertMessage(email2, raw, options = {}) {
2056
+ return insertMessage(this, email2, raw, options);
2731
2057
  }
2732
- sendMessage(email3, raw, options = {}) {
2733
- return sendMessage(this, email3, raw, options);
2058
+ sendMessage(email2, raw, options = {}) {
2059
+ return sendMessage(this, email2, raw, options);
2734
2060
  }
2735
- createDraft(email3, raw, options = {}) {
2736
- return createDraft(this, email3, raw, options);
2061
+ createDraft(email2, raw, options = {}) {
2062
+ return createDraft(this, email2, raw, options);
2737
2063
  }
2738
- createComposedDraft(email3, input) {
2739
- return createComposedDraft(this, email3, input);
2064
+ createComposedDraft(email2, input) {
2065
+ return createComposedDraft(this, email2, input);
2740
2066
  }
2741
- listDrafts(email3, query = "") {
2742
- return listDrafts(this, email3, query);
2067
+ listDrafts(email2, query = "") {
2068
+ return listDrafts(this, email2, query);
2743
2069
  }
2744
- drafts(email3, query = "", includeTrash = false) {
2745
- return drafts(this, email3, query, includeTrash);
2070
+ drafts(email2, query = "", includeTrash = false) {
2071
+ return drafts(this, email2, query, includeTrash);
2746
2072
  }
2747
- draft(email3, draftId) {
2748
- return draft(this, email3, draftId);
2073
+ draft(email2, draftId) {
2074
+ return draft(this, email2, draftId);
2749
2075
  }
2750
- updateDraft(email3, draftId, raw, options = {}) {
2751
- return updateDraft(this, email3, draftId, raw, options);
2076
+ updateDraft(email2, draftId, raw, options = {}) {
2077
+ return updateDraft(this, email2, draftId, raw, options);
2752
2078
  }
2753
- sendDraft(email3, draftId) {
2754
- return sendDraft(this, email3, draftId);
2079
+ sendDraft(email2, draftId) {
2080
+ return sendDraft(this, email2, draftId);
2755
2081
  }
2756
- deleteDraft(email3, draftId) {
2757
- deleteDraft(this, email3, draftId);
2082
+ deleteDraft(email2, draftId) {
2083
+ deleteDraft(this, email2, draftId);
2758
2084
  }
2759
- modifyMessageLabels(email3, messageId2, add = [], remove = []) {
2760
- return modifyMessageLabels(this, email3, messageId2, add, remove);
2085
+ modifyMessageLabels(email2, messageId, add = [], remove = []) {
2086
+ return modifyMessageLabels(this, email2, messageId, add, remove);
2761
2087
  }
2762
- modifyThreadLabels(email3, threadId, add = [], remove = []) {
2763
- return modifyThreadLabels(this, email3, threadId, add, remove);
2088
+ modifyThreadLabels(email2, threadId, add = [], remove = []) {
2089
+ return modifyThreadLabels(this, email2, threadId, add, remove);
2764
2090
  }
2765
- deleteMessage(email3, messageId2) {
2766
- deleteMessage(this, email3, messageId2);
2091
+ deleteMessage(email2, messageId) {
2092
+ deleteMessage(this, email2, messageId);
2767
2093
  }
2768
- batchDeleteMessages(email3, messageIds2) {
2769
- batchDeleteMessages(this, email3, messageIds2);
2094
+ batchDeleteMessages(email2, messageIds2) {
2095
+ batchDeleteMessages(this, email2, messageIds2);
2770
2096
  }
2771
- deleteThread(email3, threadId) {
2772
- deleteThread(this, email3, threadId);
2097
+ deleteThread(email2, threadId) {
2098
+ deleteThread(this, email2, threadId);
2773
2099
  }
2774
- headers(email3, messageId2) {
2775
- return headers(this, email3, messageId2);
2100
+ headers(email2, messageId) {
2101
+ return headers(this, email2, messageId);
2776
2102
  }
2777
- attachment(email3, messageId2, attachmentId) {
2778
- return attachment(this, email3, messageId2, attachmentId);
2103
+ attachment(email2, messageId, attachmentId) {
2104
+ return attachment(this, email2, messageId, attachmentId);
2779
2105
  }
2780
- applyInternalDateSource(email3, messageId2, source) {
2781
- return applyInternalDateSource(this, email3, messageId2, source);
2106
+ applyInternalDateSource(email2, messageId, source) {
2107
+ return applyInternalDateSource(this, email2, messageId, source);
2782
2108
  }
2783
- createLabel(email3, name, color) {
2784
- return createLabel(this, email3, name, color);
2109
+ createLabel(email2, name, color) {
2110
+ return createLabel(this, email2, name, color);
2785
2111
  }
2786
- listUserLabels(email3) {
2787
- return listUserLabels(this, email3);
2112
+ listUserLabels(email2) {
2113
+ return listUserLabels(this, email2);
2788
2114
  }
2789
- labels(email3) {
2790
- return labels(this, email3);
2115
+ labels(email2) {
2116
+ return labels(this, email2);
2791
2117
  }
2792
- label(email3, labelId) {
2793
- return label(this, email3, labelId);
2118
+ label(email2, labelId) {
2119
+ return label(this, email2, labelId);
2794
2120
  }
2795
- updateLabel(email3, labelId, input, replace) {
2796
- return updateLabel(this, email3, labelId, input, replace);
2121
+ updateLabel(email2, labelId, input, replace) {
2122
+ return updateLabel(this, email2, labelId, input, replace);
2797
2123
  }
2798
- deleteLabel(email3, labelId) {
2799
- deleteLabel(this, email3, labelId);
2124
+ deleteLabel(email2, labelId) {
2125
+ deleteLabel(this, email2, labelId);
2800
2126
  }
2801
- filters(email3) {
2802
- return filters(this, email3);
2127
+ filters(email2) {
2128
+ return filters(this, email2);
2803
2129
  }
2804
- filter(email3, filterId) {
2805
- return filter(this, email3, filterId);
2130
+ filter(email2, filterId) {
2131
+ return filter(this, email2, filterId);
2806
2132
  }
2807
- createFilter(email3, criteria = {}, action = {}) {
2808
- return createFilter(this, email3, criteria, action);
2133
+ createFilter(email2, criteria = {}, action = {}) {
2134
+ return createFilter(this, email2, criteria, action);
2809
2135
  }
2810
- deleteFilter(email3, filterId) {
2811
- deleteFilter(this, email3, filterId);
2136
+ deleteFilter(email2, filterId) {
2137
+ deleteFilter(this, email2, filterId);
2812
2138
  }
2813
- listHistory(email3, startHistoryId, options = {}) {
2814
- return listHistory(this, email3, startHistoryId, options);
2139
+ listHistory(email2, startHistoryId, options = {}) {
2140
+ return listHistory(this, email2, startHistoryId, options);
2815
2141
  }
2816
- profile(email3) {
2817
- return profile(this, email3);
2142
+ profile(email2) {
2143
+ return profile(this, email2);
2818
2144
  }
2819
- currentHistoryIdFor(email3) {
2820
- return currentHistoryIdFor(this, email3);
2145
+ currentHistoryIdFor(email2) {
2146
+ return currentHistoryIdFor(this, email2);
2821
2147
  }
2822
- latestMessageHistory(email3, messageId2) {
2823
- return latestMessageHistory(this, email3, messageId2);
2148
+ latestMessageHistory(email2, messageId) {
2149
+ return latestMessageHistory(this, email2, messageId);
2824
2150
  }
2825
- latestThreadHistory(email3, threadId) {
2826
- return latestThreadHistory(this, email3, threadId);
2151
+ latestThreadHistory(email2, threadId) {
2152
+ return latestThreadHistory(this, email2, threadId);
2827
2153
  }
2828
- forwardingAddresses(email3) {
2829
- return forwardingAddresses(this, email3);
2154
+ forwardingAddresses(email2) {
2155
+ return forwardingAddresses(this, email2);
2830
2156
  }
2831
- forwardingAddress(email3, forwardingEmail) {
2832
- return forwardingAddress(this, email3, forwardingEmail);
2157
+ forwardingAddress(email2, forwardingEmail) {
2158
+ return forwardingAddress(this, email2, forwardingEmail);
2833
2159
  }
2834
- sendAs(email3) {
2835
- return sendAs(this, email3);
2160
+ sendAs(email2) {
2161
+ return sendAs(this, email2);
2836
2162
  }
2837
- sendAsAddress(email3, sendAsEmail) {
2838
- return sendAsAddress(this, email3, sendAsEmail);
2163
+ sendAsAddress(email2, sendAsEmail) {
2164
+ return sendAsAddress(this, email2, sendAsEmail);
2839
2165
  }
2840
- searchMessages(email3, query = "", options = {}) {
2841
- return searchMessages(this, email3, query, options);
2166
+ searchMessages(email2, query = "", options = {}) {
2167
+ return searchMessages(this, email2, query, options);
2842
2168
  }
2843
- searchThreads(email3, query = "", options = {}) {
2844
- return searchThreads(this, email3, query, options);
2169
+ searchThreads(email2, query = "", options = {}) {
2170
+ return searchThreads(this, email2, query, options);
2845
2171
  }
2846
2172
  exportState() {
2847
2173
  return exportGmailState(this.db);
2848
2174
  }
2849
2175
  };
2850
2176
 
2851
- // ../packages/twin-gmail/dist/src/check-params.js
2852
- var messageId = {
2853
- name: "message",
2854
- pattern: "[A-Za-z0-9_-]{1,128}",
2855
- example: "msg_support",
2856
- render: (value) => value,
2857
- parse: (raw) => raw
2858
- };
2859
- var labelRef = {
2860
- name: "label",
2861
- pattern: "[A-Za-z0-9_-]{1,128}",
2862
- example: "STARRED",
2863
- render: (value) => value,
2864
- parse: (raw) => raw
2865
- };
2866
- var labelName = {
2867
- name: "label",
2868
- pattern: "[A-Za-z0-9][A-Za-z0-9 _-]{0,127}",
2869
- example: "Parity Complete",
2870
- render: (value) => value,
2871
- parse: (raw) => raw
2872
- };
2873
- var emailAddress = {
2874
- name: "email",
2875
- pattern: "[^\\s@\"'`]+@[^\\s@\"'`]+",
2876
- example: "alice@example.com",
2877
- render: (value) => value,
2878
- parse: (raw) => raw
2879
- };
2880
- var mailboxRef = {
2881
- name: "mailbox",
2882
- pattern: "[^\\s@\"'`]+@[^\\s@\"'`]+",
2883
- example: "pome-agent@pome-twin.test",
2884
- render: (value) => value,
2885
- parse: (raw) => raw
2886
- };
2887
- var exactCount = {
2888
- name: "count",
2889
- pattern: "\\d{1,9}",
2890
- example: "5",
2891
- render: (value) => value,
2892
- parse: (raw) => raw
2893
- };
2894
- var countWord = {
2895
- name: "count",
2896
- pattern: "(?:\\d{1,9}|zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)",
2897
- example: "two",
2898
- render: (value) => value,
2899
- parse: (raw) => raw
2900
- };
2901
- var NUMBER_WORDS = {
2902
- zero: 0,
2903
- one: 1,
2904
- two: 2,
2905
- three: 3,
2906
- four: 4,
2907
- five: 5,
2908
- six: 6,
2909
- seven: 7,
2910
- eight: 8,
2911
- nine: 9,
2912
- ten: 10,
2913
- eleven: 11,
2914
- twelve: 12
2915
- };
2916
- function parseCount(raw) {
2917
- const trimmed = raw.trim().toLowerCase();
2918
- if (/^\d+$/.test(trimmed))
2919
- return Number(trimmed);
2920
- return NUMBER_WORDS[trimmed] ?? null;
2921
- }
2922
-
2923
- // ../packages/twin-gmail/dist/src/check-state.js
2924
- var MESSAGES_PATH = statePath("messages");
2925
- var DRAFTS_PATH = statePath("drafts");
2926
- var LABELS_PATH = statePath("labels");
2927
- var MESSAGE_LABELS_PATH = statePath("messageLabels");
2928
- function missSkip(miss) {
2929
- const outcome = { passed: false, status: "skipped", reason: miss.missing };
2930
- if (miss.searched === void 0)
2931
- return outcome;
2932
- return { ...outcome, evidenceStatePaths: [miss.searched] };
2933
- }
2934
- var lower = (value) => (value ?? "").toLowerCase();
2935
- function isTruncated(state, collection) {
2936
- return (state.exportBounds?.truncatedCollections ?? []).includes(collection);
2937
- }
2938
- function resolveMessage(state, id2) {
2939
- if (state.messages == null)
2940
- return { missing: "state_incomplete" };
2941
- const indices = state.messages.map((message2, index) => lower(message2.id) === lower(id2) ? index : -1).filter((index) => index >= 0);
2942
- if (indices.length > 1) {
2943
- return {
2944
- missing: `message_ambiguous ("${id2}" exists in ${indices.length} mailboxes)`,
2945
- searched: MESSAGES_PATH
2946
- };
2947
- }
2948
- if (indices.length === 1) {
2949
- const index = indices[0];
2950
- return { found: state.messages[index], path: childStatePath(MESSAGES_PATH, index) };
2951
- }
2952
- if (isTruncated(state, "messages")) {
2953
- return { missing: 'collection_truncated ("messages")', searched: MESSAGES_PATH };
2954
- }
2955
- return { missing: `message_not_found ("${id2}")`, searched: MESSAGES_PATH };
2956
- }
2957
- function labelIdsFor(state, wanted) {
2958
- const ids = /* @__PURE__ */ new Set([lower(wanted)]);
2959
- for (const label2 of state.labels ?? []) {
2960
- const matches = lower(label2.name) === lower(wanted) || lower(label2.id) === lower(wanted);
2961
- if (matches && label2.id != null)
2962
- ids.add(lower(label2.id));
2963
- }
2964
- return ids;
2965
- }
2966
- function messageCarriesLabel(state, messageId2, wanted) {
2967
- if (state.messageLabels == null)
2968
- return { missing: "state_incomplete" };
2969
- const ids = labelIdsFor(state, wanted);
2970
- const carried = state.messageLabels.some((row) => lower(row.messageId) === lower(messageId2) && ids.has(lower(row.labelId)));
2971
- if (!carried && isTruncated(state, "messageLabels")) {
2972
- return { missing: 'collection_truncated ("messageLabels")', searched: MESSAGE_LABELS_PATH };
2973
- }
2974
- return { found: carried, path: MESSAGE_LABELS_PATH };
2975
- }
2976
- function resolveLabelByName(state, name) {
2977
- if (state.labels == null)
2978
- return { missing: "state_incomplete" };
2979
- const index = state.labels.findIndex((label2) => lower(label2.name) === lower(name));
2980
- if (index >= 0) {
2981
- return { found: state.labels[index], path: childStatePath(LABELS_PATH, index) };
2982
- }
2983
- if (isTruncated(state, "labels")) {
2984
- return { missing: 'collection_truncated ("labels")', searched: LABELS_PATH };
2985
- }
2986
- return { missing: `label_not_found ("${name}")`, searched: LABELS_PATH };
2987
- }
2988
- function draftRecipients(state, draft3) {
2989
- const message2 = (state.messages ?? []).find((candidate) => lower(candidate.id) === lower(draft3.messageId));
2990
- if (!message2)
2991
- return [];
2992
- return [...message2.to ?? [], ...message2.cc ?? []];
2993
- }
2994
-
2995
- // ../packages/twin-gmail/dist/src/check-worlds.js
2996
- var FIXTURE_MAILBOX = "pome-agent@pome-twin.test";
2997
- function gmailState(parts = {}) {
2998
- return {
2999
- mailboxes: [{ email: FIXTURE_MAILBOX }],
3000
- messages: [],
3001
- drafts: [],
3002
- labels: [],
3003
- messageLabels: [],
3004
- exportBounds: { messageBodiesOmitted: true, largeMailbox: false, truncatedCollections: [] },
3005
- ...parts
3006
- };
3007
- }
3008
- function finalWorld(final) {
3009
- return { seed: null, final, tape: null };
3010
- }
3011
- function tapeWorld(tape) {
3012
- return { seed: null, final: gmailState(), tape };
3013
- }
3014
- function message(id2, parts = {}) {
3015
- return { mailboxEmail: FIXTURE_MAILBOX, id: id2, to: [FIXTURE_MAILBOX], bodyOmitted: true, ...parts };
3016
- }
3017
- function systemLabel(name) {
3018
- return { mailboxEmail: FIXTURE_MAILBOX, id: name, name, type: "system" };
3019
- }
3020
- function userLabel(id2, name) {
3021
- return { mailboxEmail: FIXTURE_MAILBOX, id: id2, name, type: "user" };
3022
- }
3023
- function messageLabel(messageId2, labelId) {
3024
- return { mailboxEmail: FIXTURE_MAILBOX, messageId: messageId2, labelId };
3025
- }
3026
- function draft2(id2, messageId2) {
3027
- return { mailboxEmail: FIXTURE_MAILBOX, id: id2, messageId: messageId2 };
3028
- }
3029
- function draftAddressedTo(id2, recipients) {
3030
- const messageId2 = `${id2}_message`;
3031
- return {
3032
- draft: draft2(id2, messageId2),
3033
- message: message(messageId2, { to: recipients })
3034
- };
3035
- }
3036
-
3037
- // ../packages/twin-gmail/dist/src/check-drafts.js
3038
- var draftAddressedTo2 = defineCheck({
3039
- id: "gmail.draft-addressed-to",
3040
- description: "Joins every draft to its backing message and asks whether any of them addresses this recipient in `to` or `cc`, compared case-insensitively as an EXACT address rather than a substring. It asserts nothing about the draft's body \u2014 message bodies are digested out of the state export unconditionally, so no check on this twin can read one \u2014 and nothing about whether the draft was left unsent, which is a separate claim. A draft whose backing message did not survive the export contributes no recipients rather than throwing.",
3041
- template: "A draft addressed to {email} exists",
3042
- params: { email: emailAddress },
3043
- substrate: "final",
3044
- // An achievement: task 22's seed ships one draft addressed to bob, so an
3045
- // examinee that does nothing does not satisfy this.
3046
- polarity: () => "positive",
3047
- //
3048
- // THE LOAD-BEARING DECLARATION ON THIS CHECK.
3049
- //
3050
- // pome-cloud's `corpus.ts` has carried a prediction about this exact criterion
3051
- // since F-1028: `22-gmail-inbox-triage`'s `alice@example.com` "becomes visible
3052
- // here as 'unguarded' the day a gmail draft-recipient predicate lands without a
3053
- // `subject`, which is precisely when it starts to matter." An email address is
3054
- // squarely inside what a team's redaction config may destroy. Without this
3055
- // field the evaluator has no way to turn an impossible comparison into an
3056
- // honest skip, so the criterion scores a vacuous verdict at both doors
3057
- // instead — and the count of unguarded hazards must stay at zero.
3058
- subject: ({ email: email3 }) => email3,
3059
- // The address is the scanned literal, so the mutant points at it. It stays
3060
- // email-shaped so it re-binds to this same check: a mutant that stops matching
3061
- // evaluates to `unmatched`, which reads as "the verdict moved -> healthy" and
3062
- // hands the criterion a clean bill it did not earn.
3063
- vacuityMutant: (args) => ({ ...args, email: `${VACUITY_SENTINEL}@example.invalid` }),
3064
- discriminatingWorlds: ({ email: email3 }) => {
3065
- const wanted = draftAddressedTo("draft_target", [email3]);
3066
- const other = draftAddressedTo("draft_other", ["someone-else@example.com"]);
3067
- return {
3068
- passing: finalWorld(gmailState({ drafts: [wanted.draft], messages: [wanted.message] })),
3069
- failing: finalWorld(gmailState({ drafts: [other.draft], messages: [other.message] }))
3070
- };
3071
- },
3072
- evaluate({ email: email3 }, { final }) {
3073
- if (final.drafts == null || final.messages == null) {
3074
- return { passed: false, status: "skipped", reason: "state_incomplete" };
3075
- }
3076
- const wanted = email3.toLowerCase();
3077
- const index = final.drafts.findIndex((entry) => draftRecipients(final, entry).some((to) => to.toLowerCase() === wanted));
3078
- if (index >= 0) {
3079
- const hit = final.drafts[index];
3080
- return {
3081
- passed: true,
3082
- reason: `draft ${hit.id ?? "?"} is addressed to ${email3}`,
3083
- // The DRAFT row, not the backing message that actually holds the
3084
- // address. A reader following this pointer lands on the thing the
3085
- // sentence is about — "a draft addressed to X" — and the join to the
3086
- // message is an implementation detail of how the twin exports, not the
3087
- // claim (F-1197).
3088
- evidenceStatePaths: [childStatePath(DRAFTS_PATH, index)]
3089
- };
3090
- }
3091
- if (isTruncated(final, "drafts") || isTruncated(final, "messages")) {
3092
- return { passed: false, status: "skipped", reason: "collection_truncated" };
3093
- }
3094
- return {
3095
- passed: false,
3096
- reason: `no draft is addressed to ${email3} (${final.drafts.length} draft(s) inspected)`,
3097
- evidenceStatePaths: [DRAFTS_PATH]
3098
- };
3099
- }
3100
- });
3101
- var draftCountAtLeast = defineCheck({
3102
- id: "gmail.draft-count-at-least",
3103
- description: "Counts the rows in the exported `drafts` collection and asserts there are AT LEAST this many \u2014 a lower bound, so a mailbox with more drafts than asked still passes. It counts drafts the SEED placed there as well as any the examinee created, which means a criterion whose number the seed already satisfies can be passed by an agent that does nothing. That is a property of the task, not of this check, and `measure-criterion-discrimination` is where it surfaces.",
3104
- template: "At least {count} drafts exist",
3105
- params: { count: countWord },
3106
- substrate: "final",
3107
- polarity: () => "positive",
3108
- // A threshold, not a literal hunted for inside state — there is nothing here a
3109
- // redactor could silently delete.
3110
- subject: () => null,
3111
- //
3112
- // The NUMERIC sentinel, and the argument D10 demands before it may be used.
3113
- //
3114
- // In every other pattern a numeric capture is a SELECTOR — an issue number, a
3115
- // PR number — that the predicate RESOLVES before it scans anything, so
3116
- // falsifying it early-returns "not found" and moves the verdict for a reason
3117
- // that never reaches the assertion. Here the count is the ONLY slot and there
3118
- // is nothing to resolve: it is compared directly against a cardinality the
3119
- // predicate computes, so falsifying it moves the verdict THROUGH the
3120
- // assertion. That is the same argument `stripe.payment-intent-amount` makes,
3121
- // and it is why this check is the second entry in pome-cloud's
3122
- // `NUMERIC_SENTINEL_ALLOWED`.
3123
- vacuityMutant: (args) => ({ ...args, count: String(VACUITY_SENTINEL_NUMBER) }),
3124
- discriminatingWorlds: ({ count }) => {
3125
- const wanted = parseCount(count) ?? 1;
3126
- const build = (n) => {
3127
- const pairs = Array.from({ length: n }, (_, i) => draftAddressedTo(`draft_${i}`, ["a@example.com"]));
3128
- return gmailState({
3129
- drafts: pairs.map((p) => p.draft),
3130
- messages: pairs.map((p) => p.message)
3131
- });
3132
- };
3133
- return { passing: finalWorld(build(wanted)), failing: finalWorld(build(Math.max(0, wanted - 1))) };
3134
- },
3135
- evaluate({ count }, { final }) {
3136
- const wanted = parseCount(count);
3137
- if (wanted === null) {
3138
- return { passed: false, status: "skipped", reason: `uncountable ("${count}")` };
3139
- }
3140
- if (final.drafts == null)
3141
- return { passed: false, status: "skipped", reason: "state_incomplete" };
3142
- const total = final.drafts.length;
3143
- if (total < wanted && isTruncated(final, "drafts")) {
3144
- return { passed: false, status: "skipped", reason: 'collection_truncated ("drafts")' };
3145
- }
3146
- return {
3147
- passed: total >= wanted,
3148
- reason: `${total} draft(s) exist (wanted at least ${wanted})`,
3149
- // The collection whose LENGTH is the assertion. There is no narrower
3150
- // address for a cardinality.
3151
- evidenceStatePaths: [DRAFTS_PATH]
3152
- };
3153
- }
3154
- });
3155
-
3156
- // ../packages/twin-gmail/dist/src/check-labels.js
3157
- var labelExists = defineCheck({
3158
- id: "gmail.label-exists",
3159
- description: "Asks whether the mailbox defines a label with this DISPLAY NAME, compared case-insensitively the way the twin's own seeder keys its uniqueness lookup. It reads the `labels` collection only \u2014 a label that exists but has been applied to nothing still passes, and a message carrying a label is `gmail.message-has-label`'s question, not this one. It deliberately does NOT match on the minted id: `Label_follow_up` is an id and `Follow Up` is the name of that same label, and letting one sentence mean both would make the assertion unreadable.",
3160
- // The name slot admits spaces, which is what the corpus needs (`Parity
3161
- // Complete`) and what separates this template from `gmail.message-has-label`'s
3162
- // id slot. The two cannot claim one sentence: this one says "A label named",
3163
- // that one says "Message ... has label".
3164
- template: "A label named {label} exists",
3165
- params: { label: labelName },
3166
- substrate: "final",
3167
- // An achievement — task 23's seed defines no `Parity Complete`, so the
3168
- // examinee has to create it.
3169
- polarity: () => "positive",
3170
- // The name is a caller-supplied literal compared against state.
3171
- subject: ({ label: label2 }) => label2,
3172
- vacuityMutant: (args) => ({ ...args, label: VACUITY_SENTINEL }),
3173
- discriminatingWorlds: ({ label: label2 }) => ({
3174
- // Both worlds carry a non-empty `labels` collection, so the failing world
3175
- // fails on the ASSERTION (`label_not_found`) rather than reproducing the
3176
- // `state_incomplete` an empty world gives — the degenerate arm the probe
3177
- // rejects.
3178
- passing: finalWorld(gmailState({ labels: [systemLabel("INBOX"), userLabel("Label_1", label2)] })),
3179
- failing: finalWorld(gmailState({ labels: [systemLabel("INBOX")] }))
3180
- }),
3181
- evaluate({ label: label2 }, { final }) {
3182
- const found = resolveLabelByName(final, label2);
3183
- if ("missing" in found) {
3184
- if (found.missing.startsWith("label_not_found")) {
3185
- return {
3186
- passed: false,
3187
- reason: `no label named "${label2}" exists`,
3188
- // The label COLLECTION — the honest citation for a lookup that found
3189
- // nothing, and the arm a reader most wants to open: see for yourself
3190
- // that the name is not in it (F-1197).
3191
- ...found.searched === void 0 ? {} : { evidenceStatePaths: [found.searched] }
3192
- };
3193
- }
3194
- return missSkip(found);
3195
- }
3196
- return {
3197
- passed: true,
3198
- reason: `label "${label2}" exists (id ${found.found.id ?? "?"})`,
3199
- evidenceStatePaths: [found.path]
3200
- };
3201
- }
3202
- });
3203
-
3204
- // ../packages/twin-gmail/dist/src/check-messages.js
3205
- var messageHasLabel = defineCheck({
3206
- id: "gmail.message-has-label",
3207
- description: "Looks the message up by id, then asks the `messageLabels` JOIN whether any row links it to the named label. The label may be given as its minted id (`Label_follow_up`) or as the display name of the same label (`Follow Up`); a system label carries both as one string (`INBOX`, `STARRED`). It asserts nothing about the message's OTHER labels \u2014 a message carrying the named label plus five more passes. A message the export does not carry is a SKIP, not a fail, and so is a label collection the export truncated: absent from a capped list is not absent.",
3208
- template: "Message {message} has label {label}",
3209
- params: { message: messageId, label: labelRef },
3210
- substrate: "final",
3211
- // An achievement. The seed leaves `msg_support` on INBOX/UNREAD only, so the
3212
- // examinee has to act for this to hold.
3213
- polarity: () => "positive",
3214
- // The label is a caller-supplied literal compared against state, so it is
3215
- // declared. No first-party redactor pattern touches a Gmail label id, which is
3216
- // exactly why declaring it keeps that a verified fact rather than an
3217
- // assumption — a team's own redaction config is not first-party.
3218
- subject: ({ label: label2 }) => label2,
3219
- // The message id only SELECTS; falsifying it moves the verdict through the
3220
- // lookup rather than the assertion. The label is the scanned literal.
3221
- vacuityMutant: (args) => ({ ...args, label: VACUITY_SENTINEL }),
3222
- discriminatingWorlds: ({ message: id2, label: label2 }) => {
3223
- const labels2 = [systemLabel("INBOX"), userLabel(label2, label2)];
3224
- const base = { messages: [message(id2)], labels: labels2 };
3225
- return {
3226
- passing: finalWorld(gmailState({ ...base, messageLabels: [messageLabel(id2, "INBOX"), messageLabel(id2, label2)] })),
3227
- failing: finalWorld(gmailState({ ...base, messageLabels: [messageLabel(id2, "INBOX")] }))
3228
- };
3229
- },
3230
- evaluate({ message: id2, label: label2 }, { final }) {
3231
- const found = resolveMessage(final, id2);
3232
- if ("missing" in found)
3233
- return missSkip(found);
3234
- const carried = messageCarriesLabel(final, id2, label2);
3235
- if ("missing" in carried)
3236
- return missSkip(carried);
3237
- return {
3238
- passed: carried.found,
3239
- reason: carried.found ? `message ${id2} carries label ${label2}` : `message ${id2} does not carry label ${label2}`,
3240
- // BOTH halves of the lookup, because the predicate really does read two
3241
- // places: the message row it resolved by id, and the join table it then
3242
- // questioned. Labels are not nested under their message in this export —
3243
- // that is the whole reason `messageCarriesLabel` exists — so one pointer
3244
- // could not say where this verdict came from (F-1197).
3245
- evidenceStatePaths: [found.path, carried.path]
3246
- };
3247
- }
3248
- });
3249
- var mailboxLabelCount = defineCheck({
3250
- id: "gmail.mailbox-label-count",
3251
- description: "Counts the messages in the named mailbox that the `messageLabels` JOIN links to the named label, and asserts the total is EXACTLY the number given. Not 'at least' \u2014 a mailbox with six SENT messages fails a criterion asking for five, which is what makes it able to catch a duplicate send. A mailbox the export lists but does not contain is a SKIP; so is a truncated collection.",
3252
- template: "The mailbox `{mailbox}` has exactly {count} messages labeled {label}",
3253
- params: { mailbox: mailboxRef, count: exactCount, label: labelRef },
3254
- substrate: "final",
3255
- // The count carries the direction: asserting zero is a prohibition, any
3256
- // other count is work the examinee must do. Preserved from the legacy rule,
3257
- // which computed the same thing per match.
3258
- polarity: ({ count }) => Number(count) === 0 ? "negative" : "positive",
3259
- subject: ({ label: label2 }) => label2,
3260
- // The mailbox resolves and the count is compared to a derived total; the
3261
- // label is the literal actually scanned in state. Pointing the mutant at the
3262
- // count would falsify a threshold rather than a lookup — see
3263
- // `gmail.draft-count-at-least`, where the count IS the only slot.
3264
- vacuityMutant: (args) => ({ ...args, label: VACUITY_SENTINEL }),
3265
- discriminatingWorlds: ({ mailbox, count, label: label2 }) => {
3266
- const wanted = Number(count);
3267
- const world = (n) => {
3268
- const messages = Array.from({ length: n }, (_, i) => message(`msg_${i}`, { mailboxEmail: mailbox }));
3269
- return gmailState({
3270
- mailboxes: [{ email: mailbox }],
3271
- messages,
3272
- labels: [systemLabel(label2)],
3273
- messageLabels: messages.map((m) => ({ mailboxEmail: mailbox, messageId: m.id, labelId: label2 }))
3274
- });
3275
- };
3276
- return { passing: finalWorld(world(wanted)), failing: finalWorld(world(wanted + 1)) };
3277
- },
3278
- evaluate({ mailbox, count, label: label2 }, { final }) {
3279
- const wanted = Number(count);
3280
- if (final.messages == null || final.messageLabels == null) {
3281
- return { passed: false, status: "skipped", reason: "state_incomplete" };
3282
- }
3283
- if (isTruncated(final, "messages") || isTruncated(final, "messageLabels")) {
3284
- return { passed: false, status: "skipped", reason: "collection_truncated" };
3285
- }
3286
- if (final.mailboxes != null && !final.mailboxes.some((mb) => (mb.email ?? "").toLowerCase() === mailbox.toLowerCase())) {
3287
- return { passed: false, status: "skipped", reason: `mailbox_not_found ("${mailbox}")` };
3288
- }
3289
- const ids = labelIdsFor(final, label2);
3290
- const labeled = new Set(final.messageLabels.filter((row) => ids.has((row.labelId ?? "").toLowerCase())).map((row) => (row.messageId ?? "").toLowerCase()));
3291
- const total = final.messages.filter((msg) => (msg.mailboxEmail ?? "").toLowerCase() === mailbox.toLowerCase() && msg.id != null && labeled.has(msg.id.toLowerCase())).length;
3292
- return {
3293
- passed: total === wanted,
3294
- reason: `mailbox "${mailbox}" has ${total} message(s) labeled ${label2} (wanted ${wanted})`,
3295
- // The two collections the count is computed FROM. The count itself lives
3296
- // nowhere in the tree, so there is no narrower honest address — and the
3297
- // guards above already established that both collections are present and
3298
- // un-capped, which is what makes these pointers resolve.
3299
- evidenceStatePaths: [MESSAGES_PATH, MESSAGE_LABELS_PATH]
3300
- };
3301
- }
3302
- });
3303
- var oneMessagePerRecipient = defineCheck({
3304
- id: "gmail.one-message-per-recipient",
3305
- description: "Flattens every addressee across the messages carrying the named label and asserts each address appears EXACTLY ONCE \u2014 and, when a count is named, that there are that many messages, that many distinct recipients, and no more addressee slots than that. It is the assertion that separates 'sent to everyone' from 'sent to everyone, some of them twice', which an exact total alone cannot do.",
3306
- template: "Exactly one {label} message is addressed to each of the {count} recipients",
3307
- params: { label: labelRef, count: countWord },
3308
- substrate: "final",
3309
- polarity: () => "positive",
3310
- subject: ({ label: label2 }) => label2,
3311
- // The label is scanned; the count is a declared arity compared to a derived
3312
- // one. Same argument as `mailboxLabelCount`.
3313
- vacuityMutant: (args) => ({ ...args, label: VACUITY_SENTINEL }),
3314
- discriminatingWorlds: ({ label: label2, count }) => {
3315
- const wanted = parseCount(count) ?? 1;
3316
- const recipients = Array.from({ length: wanted }, (_, i) => `user${i}@example.com`);
3317
- const build = (addressees) => {
3318
- const messages = addressees.map((to, i) => message(`msg_${i}`, { to }));
3319
- return finalWorld(gmailState({
3320
- messages,
3321
- labels: [systemLabel(label2)],
3322
- messageLabels: messages.map((m) => messageLabel(m.id, label2))
3323
- }));
3324
- };
3325
- return {
3326
- passing: build(recipients.map((r) => [r])),
3327
- // The DUPLICATE SEND, not a missing one: the same number of messages, one
3328
- // recipient served twice and another not at all. A world with fewer
3329
- // messages would fail on arity, which the exact-count check already covers.
3330
- failing: build([[recipients[0]], ...recipients.slice(2).map((r) => [r]), [recipients[0]]])
3331
- };
3332
- },
3333
- evaluate({ label: label2, count }, { final }) {
3334
- const wanted = parseCount(count);
3335
- if (final.messages == null || final.messageLabels == null) {
3336
- return { passed: false, status: "skipped", reason: "state_incomplete" };
3337
- }
3338
- if (isTruncated(final, "messages") || isTruncated(final, "messageLabels")) {
3339
- return { passed: false, status: "skipped", reason: "collection_truncated" };
3340
- }
3341
- const ids = labelIdsFor(final, label2);
3342
- const labeled = new Set(final.messageLabels.filter((row) => ids.has((row.labelId ?? "").toLowerCase())).map((row) => (row.messageId ?? "").toLowerCase()));
3343
- const sent = final.messages.filter((msg) => msg.id != null && labeled.has(msg.id.toLowerCase()));
3344
- const addressees = sent.flatMap((msg) => (msg.to ?? []).map((to) => to.toLowerCase()));
3345
- const distinct = new Set(addressees);
3346
- const noDuplicates = addressees.length === distinct.size;
3347
- const arityOk = wanted == null ? sent.length === distinct.size : sent.length === wanted && distinct.size === wanted && addressees.length === wanted;
3348
- const passed = noDuplicates && arityOk;
3349
- return {
3350
- passed,
3351
- reason: passed ? `${sent.length} ${label2} message(s), one per distinct recipient, no duplicates` : `${sent.length} ${label2} message(s) to ${distinct.size} distinct recipient(s) (${addressees.length} addressee slot(s)${wanted == null ? "" : `, wanted ${wanted}`}${noDuplicates ? "" : ", duplicate send detected"})`,
3352
- // Same two collections, same reason: the duplicate this check exists to
3353
- // catch is a property of the addressee lists across `messages`, filtered
3354
- // by `messageLabels`, and neither the flattened list nor its distinct set
3355
- // is anything the tree holds.
3356
- evidenceStatePaths: [MESSAGES_PATH, MESSAGE_LABELS_PATH]
3357
- };
3358
- }
3359
- });
3360
-
3361
- // ../packages/twin-gmail/dist/src/check-tape.js
3362
- var noUnsupportedEndpoint = defineCheck({
3363
- id: "gmail.no-unsupported-endpoint",
3364
- description: 'Scans the recorded call tape for any request the twin answered with fidelity "unsupported" \u2014 a route it does not implement, answered 501. It asserts nothing about whether the run SUCCEEDED, and nothing about calls that were merely rejected: a 404 or a 422 from a route the twin does implement is a semantic answer and passes this check. The tape is scoped to this twin by the engine before the check sees it, so an unsupported call to a DIFFERENT twin in a multi-twin session cannot fail it \u2014 which matters here, because task 27 runs gmail and github together.',
3365
- // No slots, and no twin word. The legacy cloud rule accepted "No unsupported
3366
- // Gmail endpoint was called" alongside the bare form, plus plural and `were`
3367
- // variants, because an author typed English. Under position 2 an author PICKS
3368
- // the check, so those variants are retired rather than ported — the same
3369
- // decision `github.no-unsupported-endpoint` made, and this is now the same
3370
- // sentence on both twins, resolved per-twin by the engine.
3371
- template: "No unsupported endpoint was called",
3372
- params: {},
3373
- substrate: "tape",
3374
- // A prohibition. Nothing is required to happen; only the examinee reaching for
3375
- // an unimplemented route can break it.
3376
- polarity: () => "negative",
3377
- // No caller-supplied literal is hunted for in any substrate, so there is
3378
- // nothing a redactor could silently delete out from under this check.
3379
- subject: () => null,
3380
- // No slots, so the sentence carries no literal to falsify. The trigger is "a
3381
- // call with fidelity=unsupported exists", which lives on the tape and not in
3382
- // the sentence. Reported as `no_trigger`, never as clean.
3383
- vacuityMutant: () => null,
3384
- discriminatingWorlds: () => ({
3385
- passing: tapeWorld([
3386
- {
3387
- twin: "gmail",
3388
- method: "GET",
3389
- path: "/gmail/v1/users/me/messages",
3390
- status: 200,
3391
- fidelity: "semantic",
3392
- event_id: "evt_ok"
3393
- }
3394
- ]),
3395
- failing: tapeWorld([
3396
- {
3397
- twin: "gmail",
3398
- method: "POST",
3399
- path: "/gmail/v1/users/me/watch",
3400
- status: 501,
3401
- fidelity: "unsupported",
3402
- event_id: "evt_bad"
3403
- }
3404
- ])
3405
- }),
3406
- evaluate(_args, { tape }) {
3407
- if (tape === null)
3408
- return { passed: false, reason: "tape_missing", status: "skipped" };
3409
- const unsupported2 = tape.filter((event) => event.fidelity === "unsupported");
3410
- if (unsupported2.length === 0) {
3411
- return {
3412
- passed: true,
3413
- reason: `no unsupported Gmail endpoint was called (${tape.length} call(s) inspected)`
3414
- };
3415
- }
3416
- const evidenceEventIds = unsupported2.map((event) => event.event_id).filter((id2) => typeof id2 === "string" && id2 !== "");
3417
- const outcome = {
3418
- passed: false,
3419
- reason: `${unsupported2.length} unsupported Gmail call(s): [${unsupported2.map((event) => event.path ?? "?").join(", ")}]`
3420
- };
3421
- return evidenceEventIds.length > 0 ? { ...outcome, evidenceEventIds } : outcome;
3422
- }
3423
- });
3424
-
3425
- // ../packages/twin-gmail/dist/src/checks.js
3426
- var GMAIL_CHECKS = [
3427
- messageHasLabel,
3428
- labelExists,
3429
- draftAddressedTo2,
3430
- draftCountAtLeast,
3431
- mailboxLabelCount,
3432
- oneMessagePerRecipient,
3433
- // Last, because these are the only ones that assert about the RUN rather than
3434
- // the world it left behind — twin-github orders its tape checks the same way.
3435
- noUnsupportedEndpoint
3436
- ];
3437
-
3438
2177
  // ../packages/twin-gmail/dist/src/identity.js
3439
2178
  var DEFAULT_GMAIL_EMAIL = "pome-agent@pome-twin.test";
3440
2179
  function identityFromSession(session) {
@@ -4592,7 +3331,7 @@ var mcp_tools_list_canonical_default = {
4592
3331
  ]
4593
3332
  }
4594
3333
  };
4595
- var email2 = z.string().trim().email();
3334
+ var email = z.string().trim().email();
4596
3335
  var pageSize = z.number().int().min(1).max(50).optional();
4597
3336
  var pageToken = z.string().optional();
4598
3337
  var labelIds = z.array(z.string().min(1)).min(1).max(100);
@@ -4610,13 +3349,13 @@ var attachmentInputSchema = z.object({
4610
3349
  }).passthrough();
4611
3350
  var createDraftInputSchema = z.object({
4612
3351
  attachments: z.array(attachmentInputSchema).max(100).optional(),
4613
- bcc: z.array(email2).max(500).optional(),
3352
+ bcc: z.array(email).max(500).optional(),
4614
3353
  body: z.string().optional(),
4615
- cc: z.array(email2).max(500).optional(),
3354
+ cc: z.array(email).max(500).optional(),
4616
3355
  htmlBody: z.string().optional(),
4617
3356
  replyToMessageId: z.string().min(1).optional(),
4618
3357
  subject: z.string().optional(),
4619
- to: z.array(email2).max(500).optional()
3358
+ to: z.array(email).max(500).optional()
4620
3359
  }).passthrough();
4621
3360
  var listDraftsInputSchema = z.object({
4622
3361
  pageSize,
@@ -4766,9 +3505,9 @@ function decodePageToken(token, binding, snapshot, secret = resolvePageTokenSecr
4766
3505
  invalidArgument("Invalid page token");
4767
3506
  }
4768
3507
  }
4769
- function normalizeListBinding(route, email3, values) {
3508
+ function normalizeListBinding(route, email2, values) {
4770
3509
  const canonical = Object.entries(values).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => [key, Array.isArray(value) ? [...value].sort() : value]);
4771
- return createHash("sha256").update(JSON.stringify([route, email3.toLowerCase(), canonical])).digest("hex");
3510
+ return createHash("sha256").update(JSON.stringify([route, email2.toLowerCase(), canonical])).digest("hex");
4772
3511
  }
4773
3512
 
4774
3513
  // ../packages/twin-gmail/dist/src/mcp.js
@@ -4779,8 +3518,8 @@ var implementations = {
4779
3518
  mutation: true,
4780
3519
  handler: (domain, args, ctx) => mutate(domain, ctx, () => {
4781
3520
  const input = args;
4782
- const email3 = identityFromSession(ctx.session).email;
4783
- const draft3 = domain.createComposedDraft(email3, {
3521
+ const email2 = identityFromSession(ctx.session).email;
3522
+ const draft2 = domain.createComposedDraft(email2, {
4784
3523
  to: input.to,
4785
3524
  cc: input.cc,
4786
3525
  bcc: input.bcc,
@@ -4796,7 +3535,7 @@ var implementations = {
4796
3535
  data: attachment2.content
4797
3536
  }))
4798
3537
  });
4799
- return draftResult(draft3.id, draft3.message, false);
3538
+ return draftResult(draft2.id, draft2.message, false);
4800
3539
  })
4801
3540
  },
4802
3541
  list_drafts: {
@@ -4804,14 +3543,14 @@ var implementations = {
4804
3543
  mutation: false,
4805
3544
  handler: (domain, args, ctx) => {
4806
3545
  const input = args;
4807
- const email3 = identityFromSession(ctx.session).email;
4808
- const drafts2 = domain.listDrafts(email3, input.query ?? "");
4809
- const page = paginate(domain, email3, "drafts.list", drafts2, input.pageSize, input.pageToken, {
3546
+ const email2 = identityFromSession(ctx.session).email;
3547
+ const drafts2 = domain.listDrafts(email2, input.query ?? "");
3548
+ const page = paginate(domain, email2, "drafts.list", drafts2, input.pageSize, input.pageToken, {
4810
3549
  query: input.query ?? "",
4811
3550
  view: input.view ?? "DRAFT_VIEW_FULL"
4812
3551
  });
4813
3552
  return {
4814
- drafts: page.items.map((draft3) => draftResult(draft3.id, draft3.message, input.view === "DRAFT_VIEW_METADATA_ONLY")),
3553
+ drafts: page.items.map((draft2) => draftResult(draft2.id, draft2.message, input.view === "DRAFT_VIEW_METADATA_ONLY")),
4815
3554
  ...page.nextPageToken ? { nextPageToken: page.nextPageToken } : {}
4816
3555
  };
4817
3556
  }
@@ -4821,8 +3560,8 @@ var implementations = {
4821
3560
  mutation: false,
4822
3561
  handler: (domain, args, ctx) => {
4823
3562
  const input = args;
4824
- const email3 = identityFromSession(ctx.session).email;
4825
- return threadResult(domain.getThread(email3, input.threadId), normalizeMessageFormat(input.messageFormat));
3563
+ const email2 = identityFromSession(ctx.session).email;
3564
+ return threadResult(domain.getThread(email2, input.threadId), normalizeMessageFormat(input.messageFormat));
4826
3565
  }
4827
3566
  },
4828
3567
  get_message: {
@@ -4830,8 +3569,8 @@ var implementations = {
4830
3569
  mutation: false,
4831
3570
  handler: (domain, args, ctx) => {
4832
3571
  const input = args;
4833
- const email3 = identityFromSession(ctx.session).email;
4834
- return messageResult(domain.getMessage(email3, input.messageId), normalizeMessageFormat(input.messageFormat));
3572
+ const email2 = identityFromSession(ctx.session).email;
3573
+ return messageResult(domain.getMessage(email2, input.messageId), normalizeMessageFormat(input.messageFormat));
4835
3574
  }
4836
3575
  },
4837
3576
  search_threads: {
@@ -4839,11 +3578,11 @@ var implementations = {
4839
3578
  mutation: false,
4840
3579
  handler: (domain, args, ctx) => {
4841
3580
  const input = args;
4842
- const email3 = identityFromSession(ctx.session).email;
4843
- const threads = domain.searchThreads(email3, input.query ?? "", {
3581
+ const email2 = identityFromSession(ctx.session).email;
3582
+ const threads = domain.searchThreads(email2, input.query ?? "", {
4844
3583
  includeTrash: input.includeTrash
4845
3584
  });
4846
- const page = paginate(domain, email3, "threads.search", threads, input.pageSize, input.pageToken, {
3585
+ const page = paginate(domain, email2, "threads.search", threads, input.pageSize, input.pageToken, {
4847
3586
  includeTrash: input.includeTrash ?? false,
4848
3587
  query: input.query ?? "",
4849
3588
  view: input.view ?? "THREAD_VIEW_MINIMAL"
@@ -4873,8 +3612,8 @@ var implementations = {
4873
3612
  mutation: false,
4874
3613
  handler: (domain, args, ctx) => {
4875
3614
  const input = args;
4876
- const email3 = identityFromSession(ctx.session).email;
4877
- const page = paginate(domain, email3, "labels.list", domain.listUserLabels(email3), input.pageSize, input.pageToken, {});
3615
+ const email2 = identityFromSession(ctx.session).email;
3616
+ const page = paginate(domain, email2, "labels.list", domain.listUserLabels(email2), input.pageSize, input.pageToken, {});
4878
3617
  return {
4879
3618
  labels: page.items.map(labelResult),
4880
3619
  ...page.nextPageToken ? { nextPageToken: page.nextPageToken } : {}
@@ -4900,18 +3639,18 @@ var implementations = {
4900
3639
  mutation: true,
4901
3640
  handler: (domain, args, ctx) => mutate(domain, ctx, () => {
4902
3641
  const input = args;
4903
- const email3 = identityFromSession(ctx.session).email;
3642
+ const email2 = identityFromSession(ctx.session).email;
4904
3643
  if (input.autoCreateParentLabels !== false) {
4905
3644
  const parts = input.displayName.split("/");
4906
3645
  for (let index = 1; index < parts.length; index += 1) {
4907
3646
  const parent = parts.slice(0, index).join("/");
4908
- const exists = domain.listUserLabels(email3).some((label3) => label3.name.toLowerCase() === parent.toLowerCase());
3647
+ const exists = domain.listUserLabels(email2).some((label3) => label3.name.toLowerCase() === parent.toLowerCase());
4909
3648
  if (!exists)
4910
- domain.createLabel(email3, parent);
3649
+ domain.createLabel(email2, parent);
4911
3650
  }
4912
3651
  }
4913
- const created = domain.createLabel(email3, input.displayName, input.color);
4914
- const label2 = domain.listUserLabels(email3).find((item) => item.id === created.id);
3652
+ const created = domain.createLabel(email2, input.displayName, input.color);
3653
+ const label2 = domain.listUserLabels(email2).find((item) => item.id === created.id);
4915
3654
  if (!label2)
4916
3655
  throw new Error("Created label was not found");
4917
3656
  return labelResult(label2);
@@ -4968,52 +3707,52 @@ function mutate(domain, ctx, operation) {
4968
3707
  ctx.reportDelta(gmailStateDelta(before, domain.exportState()));
4969
3708
  return output;
4970
3709
  }
4971
- function draftResult(id2, message2, metadataOnly) {
3710
+ function draftResult(id, message, metadataOnly) {
4972
3711
  return {
4973
- id: id2,
4974
- threadId: message2.threadId,
4975
- toRecipients: message2.to,
4976
- ccRecipients: message2.cc,
4977
- bccRecipients: message2.bcc,
4978
- date: dateOnly(message2.internalDate),
3712
+ id,
3713
+ threadId: message.threadId,
3714
+ toRecipients: message.to,
3715
+ ccRecipients: message.cc,
3716
+ bccRecipients: message.bcc,
3717
+ date: dateOnly(message.internalDate),
4979
3718
  ...!metadataOnly ? {
4980
- subject: message2.subject,
4981
- plaintextBody: message2.text,
4982
- ...message2.html ? { htmlBody: message2.html } : {}
3719
+ subject: message.subject,
3720
+ plaintextBody: message.text,
3721
+ ...message.html ? { htmlBody: message.html } : {}
4983
3722
  } : {}
4984
3723
  };
4985
3724
  }
4986
3725
  function threadResult(thread, format) {
4987
3726
  return {
4988
3727
  id: thread.id,
4989
- messages: thread.messages.map((message2) => messageResult(message2, format))
3728
+ messages: thread.messages.map((message) => messageResult(message, format))
4990
3729
  };
4991
3730
  }
4992
- function messageResult(message2, format) {
3731
+ function messageResult(message, format) {
4993
3732
  const metadata = {
4994
- id: message2.id,
4995
- labelIds: message2.labelIds,
4996
- date: dateOnly(message2.internalDate)
3733
+ id: message.id,
3734
+ labelIds: message.labelIds,
3735
+ date: dateOnly(message.internalDate)
4997
3736
  };
4998
3737
  if (format === "metadata")
4999
3738
  return metadata;
5000
3739
  const minimal = {
5001
3740
  ...metadata,
5002
- snippet: message2.snippet,
5003
- subject: message2.subject,
5004
- sender: message2.from,
5005
- toRecipients: message2.to,
5006
- ccRecipients: message2.cc
3741
+ snippet: message.snippet,
3742
+ subject: message.subject,
3743
+ sender: message.from,
3744
+ toRecipients: message.to,
3745
+ ccRecipients: message.cc
5007
3746
  };
5008
3747
  if (format === "minimal")
5009
3748
  return minimal;
5010
3749
  return {
5011
3750
  ...minimal,
5012
- plaintextBody: message2.text,
5013
- ...message2.html ? { htmlBody: message2.html } : {},
5014
- attachmentIds: message2.attachments.map((attachment2) => attachment2.id),
5015
- ...message2.attachments.length ? {
5016
- attachments: message2.attachments.map((attachment2) => ({
3751
+ plaintextBody: message.text,
3752
+ ...message.html ? { htmlBody: message.html } : {},
3753
+ attachmentIds: message.attachments.map((attachment2) => attachment2.id),
3754
+ ...message.attachments.length ? {
3755
+ attachments: message.attachments.map((attachment2) => ({
5017
3756
  id: attachment2.id,
5018
3757
  filename: attachment2.filename,
5019
3758
  mimeType: attachment2.mimeType
@@ -5042,10 +3781,10 @@ function resolveSensitiveLabel(option) {
5042
3781
  return option;
5043
3782
  invalidArgument("labelOption must be TRASH or SPAM");
5044
3783
  }
5045
- function paginate(domain, email3, route, items, requestedSize, token, filter2) {
3784
+ function paginate(domain, email2, route, items, requestedSize, token, filter2) {
5046
3785
  const size = requestedSize ?? 20;
5047
- const snapshot = domain.currentHistoryIdFor(email3);
5048
- const binding = normalizeListBinding(route, email3, filter2);
3786
+ const snapshot = domain.currentHistoryIdFor(email2);
3787
+ const binding = normalizeListBinding(route, email2, filter2);
5049
3788
  const offset = token ? decodePageToken(token, binding, snapshot) : 0;
5050
3789
  if (offset > items.length)
5051
3790
  invalidArgument("Invalid pageToken");
@@ -5284,16 +4023,16 @@ async function readDraftSend(c) {
5284
4023
  const contentType = c.req.header("content-type") ?? "";
5285
4024
  if (/^application\/json\b/i.test(contentType) || /^text\/json\b/i.test(contentType)) {
5286
4025
  const body = await readJsonObject(c);
5287
- const id2 = stringField(body, "id");
5288
- const message2 = objectField(body, "message");
5289
- if (!id2 && !message2)
4026
+ const id = stringField(body, "id");
4027
+ const message = objectField(body, "message");
4028
+ if (!id && !message)
5290
4029
  invalidArgument("Draft id is required");
5291
4030
  return {
5292
- id: id2,
5293
- ...message2 ? {
4031
+ id,
4032
+ ...message ? {
5294
4033
  message: {
5295
- raw: stringField(message2, "raw", true),
5296
- threadId: stringField(message2, "threadId")
4034
+ raw: stringField(message, "raw", true),
4035
+ threadId: stringField(message, "threadId")
5297
4036
  }
5298
4037
  } : {}
5299
4038
  };
@@ -5363,13 +4102,13 @@ var UPLOAD = "/upload/gmail/v1/users/:userId/drafts";
5363
4102
  function registerDraftRoutes(app, kit) {
5364
4103
  const { serializers, domain } = kit;
5365
4104
  app.get(BASE, kit.read((c) => {
5366
- const email3 = emailFromContext(c);
4105
+ const email2 = emailFromContext(c);
5367
4106
  const query = c.req.query("q") ?? "";
5368
4107
  const includeSpamTrash = booleanQuery(c, "includeSpamTrash");
5369
- const drafts2 = asInputError(() => domain.drafts(email3, query, includeSpamTrash));
4108
+ const drafts2 = asInputError(() => domain.drafts(email2, query, includeSpamTrash));
5370
4109
  const maxResults = numberQuery(c, "maxResults", 100, 500);
5371
- const snapshot = domain.currentHistoryIdFor(email3);
5372
- const binding = normalizeListBinding("drafts.list", email3, { query, includeSpamTrash });
4110
+ const snapshot = domain.currentHistoryIdFor(email2);
4111
+ const binding = normalizeListBinding("drafts.list", email2, { query, includeSpamTrash });
5373
4112
  const { page, nextPageToken } = paginate2(drafts2, {
5374
4113
  maxResults,
5375
4114
  pageToken: c.req.query("pageToken"),
@@ -5379,9 +4118,9 @@ function registerDraftRoutes(app, kit) {
5379
4118
  return {
5380
4119
  body: {
5381
4120
  ...page.length ? {
5382
- drafts: page.map((draft3) => ({
5383
- id: draft3.id,
5384
- message: { id: draft3.message.id, threadId: draft3.message.threadId }
4121
+ drafts: page.map((draft2) => ({
4122
+ id: draft2.id,
4123
+ message: { id: draft2.message.id, threadId: draft2.message.threadId }
5385
4124
  }))
5386
4125
  } : {},
5387
4126
  resultSizeEstimate: drafts2.length,
@@ -5390,37 +4129,37 @@ function registerDraftRoutes(app, kit) {
5390
4129
  };
5391
4130
  }));
5392
4131
  const create = kit.write(async (c) => {
5393
- const email3 = emailFromContext(c);
4132
+ const email2 = emailFromContext(c);
5394
4133
  const input = await readMessageWrite(c, true);
5395
- const draft3 = asInputError(() => domain.createDraft(email3, input.raw, { threadId: input.threadId }));
5396
- return { body: serializers.draft(email3, draft3, "full") };
4134
+ const draft2 = asInputError(() => domain.createDraft(email2, input.raw, { threadId: input.threadId }));
4135
+ return { body: serializers.draft(email2, draft2, "full") };
5397
4136
  });
5398
4137
  app.post(BASE, create);
5399
4138
  app.post(UPLOAD, create);
5400
4139
  const send = kit.write(async (c) => {
5401
- const email3 = emailFromContext(c);
4140
+ const email2 = emailFromContext(c);
5402
4141
  const input = await readDraftSend(c);
5403
4142
  if (input.id) {
5404
4143
  if (input.message) {
5405
- asInputError(() => domain.updateDraft(email3, input.id, input.message.raw, { threadId: input.message.threadId }));
4144
+ asInputError(() => domain.updateDraft(email2, input.id, input.message.raw, { threadId: input.message.threadId }));
5406
4145
  }
5407
- const sent2 = asInputError(() => domain.sendDraft(email3, input.id));
5408
- return { body: serializers.message(email3, sent2.sender, "full") };
4146
+ const sent2 = asInputError(() => domain.sendDraft(email2, input.id));
4147
+ return { body: serializers.message(email2, sent2.sender, "full") };
5409
4148
  }
5410
- const sent = asInputError(() => domain.sendMessage(email3, input.message.raw, { threadId: input.message.threadId }));
5411
- return { body: serializers.message(email3, sent.sender, "full") };
4149
+ const sent = asInputError(() => domain.sendMessage(email2, input.message.raw, { threadId: input.message.threadId }));
4150
+ return { body: serializers.message(email2, sent.sender, "full") };
5412
4151
  });
5413
4152
  app.post(`${BASE}/send`, send);
5414
4153
  app.post(`${UPLOAD}/send`, send);
5415
4154
  app.get(`${BASE}/:id`, kit.read((c) => {
5416
- const email3 = emailFromContext(c);
5417
- return { body: serializers.draft(email3, domain.draft(email3, routeParam(c, "id")), messageFormat(c)) };
4155
+ const email2 = emailFromContext(c);
4156
+ return { body: serializers.draft(email2, domain.draft(email2, routeParam(c, "id")), messageFormat(c)) };
5418
4157
  }));
5419
4158
  const update = kit.write(async (c) => {
5420
- const email3 = emailFromContext(c);
4159
+ const email2 = emailFromContext(c);
5421
4160
  const input = await readMessageWrite(c, true);
5422
- const draft3 = asInputError(() => domain.updateDraft(email3, routeParam(c, "id"), input.raw, { threadId: input.threadId }));
5423
- return { body: serializers.draft(email3, draft3, "full") };
4161
+ const draft2 = asInputError(() => domain.updateDraft(email2, routeParam(c, "id"), input.raw, { threadId: input.threadId }));
4162
+ return { body: serializers.draft(email2, draft2, "full") };
5424
4163
  });
5425
4164
  app.put(`${BASE}/:id`, update);
5426
4165
  app.put(`${UPLOAD}/:id`, update);
@@ -5444,22 +4183,22 @@ var GmailRestSerializers = class {
5444
4183
  constructor(domain) {
5445
4184
  this.domain = domain;
5446
4185
  }
5447
- message(email3, message2, format = "full", metadataHeaders = []) {
4186
+ message(email2, message, format = "full", metadataHeaders = []) {
5448
4187
  const base = {
5449
- id: message2.id,
5450
- threadId: message2.threadId,
5451
- labelIds: message2.labelIds,
5452
- snippet: message2.snippet,
5453
- historyId: this.domain.latestMessageHistory(email3, message2.id),
5454
- internalDate: String(message2.internalDate),
5455
- sizeEstimate: message2.sizeEstimate
4188
+ id: message.id,
4189
+ threadId: message.threadId,
4190
+ labelIds: message.labelIds,
4191
+ snippet: message.snippet,
4192
+ historyId: this.domain.latestMessageHistory(email2, message.id),
4193
+ internalDate: String(message.internalDate),
4194
+ sizeEstimate: message.sizeEstimate
5456
4195
  };
5457
4196
  if (format === "minimal")
5458
4197
  return base;
5459
4198
  if (format === "raw") {
5460
- return { ...base, raw: encodeGmailRaw(this.domain.getRaw(email3, message2.id)) };
4199
+ return { ...base, raw: encodeGmailRaw(this.domain.getRaw(email2, message.id)) };
5461
4200
  }
5462
- const headers2 = this.domain.headers(email3, message2.id);
4201
+ const headers2 = this.domain.headers(email2, message.id);
5463
4202
  if (format === "metadata") {
5464
4203
  return {
5465
4204
  ...base,
@@ -5472,19 +4211,19 @@ var GmailRestSerializers = class {
5472
4211
  }
5473
4212
  };
5474
4213
  }
5475
- return { ...base, payload: fullPayload(message2, headers2) };
4214
+ return { ...base, payload: fullPayload(message, headers2) };
5476
4215
  }
5477
- thread(email3, thread, format = "full", metadataHeaders = []) {
4216
+ thread(email2, thread, format = "full", metadataHeaders = []) {
5478
4217
  const latest = thread.messages.at(-1);
5479
4218
  return {
5480
4219
  id: thread.id,
5481
- historyId: this.domain.latestThreadHistory(email3, thread.id),
4220
+ historyId: this.domain.latestThreadHistory(email2, thread.id),
5482
4221
  ...latest?.snippet ? { snippet: latest.snippet } : {},
5483
- messages: thread.messages.map((message2) => this.message(email3, message2, format, metadataHeaders))
4222
+ messages: thread.messages.map((message) => this.message(email2, message, format, metadataHeaders))
5484
4223
  };
5485
4224
  }
5486
- draft(email3, draft3, format = "full") {
5487
- return { id: draft3.id, message: this.message(email3, draft3.message, format) };
4225
+ draft(email2, draft2, format = "full") {
4226
+ return { id: draft2.id, message: this.message(email2, draft2.message, format) };
5488
4227
  }
5489
4228
  };
5490
4229
  function labelSummary(label2) {
@@ -5509,29 +4248,29 @@ function labelDetail(label2) {
5509
4248
  function historyResource(event) {
5510
4249
  if (!event.messageId || !event.threadId)
5511
4250
  return null;
5512
- const message2 = { id: event.messageId, threadId: event.threadId };
5513
- const base = { id: event.id, messages: [message2] };
4251
+ const message = { id: event.messageId, threadId: event.threadId };
4252
+ const base = { id: event.id, messages: [message] };
5514
4253
  if (event.type === "messageAdded" || event.type === "draftCreated") {
5515
- base.messagesAdded = [{ message: message2 }];
4254
+ base.messagesAdded = [{ message }];
5516
4255
  } else if (event.type === "messageDeleted" || ["draftDeleted", "draftReplaced", "draftSent"].includes(event.type)) {
5517
- base.messagesDeleted = [{ message: message2 }];
4256
+ base.messagesDeleted = [{ message }];
5518
4257
  } else if (event.type === "labelAdded") {
5519
- base.labelsAdded = [{ message: message2, labelIds: event.labelIds }];
4258
+ base.labelsAdded = [{ message, labelIds: event.labelIds }];
5520
4259
  } else if (event.type === "labelRemoved") {
5521
- base.labelsRemoved = [{ message: message2, labelIds: event.labelIds }];
4260
+ base.labelsRemoved = [{ message, labelIds: event.labelIds }];
5522
4261
  } else {
5523
4262
  return null;
5524
4263
  }
5525
4264
  return base;
5526
4265
  }
5527
- function fullPayload(message2, headers2) {
4266
+ function fullPayload(message, headers2) {
5528
4267
  const declaredType = header2(headers2, "content-type")?.split(";")[0]?.trim().toLowerCase();
5529
4268
  const contentParts = [];
5530
- if (message2.text)
5531
- contentParts.push(inlinePart(String(contentParts.length), "text/plain", message2.text));
5532
- if (message2.html)
5533
- contentParts.push(inlinePart(String(contentParts.length), "text/html", message2.html));
5534
- for (const attachment2 of message2.attachments) {
4269
+ if (message.text)
4270
+ contentParts.push(inlinePart(String(contentParts.length), "text/plain", message.text));
4271
+ if (message.html)
4272
+ contentParts.push(inlinePart(String(contentParts.length), "text/html", message.html));
4273
+ for (const attachment2 of message.attachments) {
5535
4274
  contentParts.push({
5536
4275
  partId: String(contentParts.length),
5537
4276
  mimeType: attachment2.mimeType,
@@ -5544,7 +4283,7 @@ function fullPayload(message2, headers2) {
5544
4283
  body: { attachmentId: attachment2.id, size: attachment2.size }
5545
4284
  });
5546
4285
  }
5547
- if (contentParts.length === 1 && message2.attachments.length === 0) {
4286
+ if (contentParts.length === 1 && message.attachments.length === 0) {
5548
4287
  const only = contentParts[0];
5549
4288
  return {
5550
4289
  partId: "",
@@ -5615,14 +4354,14 @@ var GmailRouteKit = class {
5615
4354
  };
5616
4355
  });
5617
4356
  }
5618
- unsupported(message2) {
4357
+ unsupported(message) {
5619
4358
  return this.context.recorder.handle({ mutation: false, fidelity: "unsupported" }, () => ({
5620
4359
  status: 501,
5621
4360
  body: {
5622
4361
  error: {
5623
4362
  code: 501,
5624
- message: message2,
5625
- errors: [{ message: message2, domain: "global", reason: "notImplemented" }],
4363
+ message,
4364
+ errors: [{ message, domain: "global", reason: "notImplemented" }],
5626
4365
  status: "UNIMPLEMENTED"
5627
4366
  }
5628
4367
  },
@@ -5637,19 +4376,19 @@ var UPLOAD2 = "/upload/gmail/v1/users/:userId/messages";
5637
4376
  function registerMessageRoutes(app, kit) {
5638
4377
  const { serializers, domain } = kit;
5639
4378
  app.get(BASE2, kit.read((c) => {
5640
- const email3 = emailFromContext(c);
4379
+ const email2 = emailFromContext(c);
5641
4380
  const query = c.req.query("q") ?? "";
5642
4381
  const includeSpamTrash = booleanQuery(c, "includeSpamTrash");
5643
4382
  const labelIds2 = repeatedQuery(c, "labelIds");
5644
- let messages = asInputError(() => domain.searchMessages(email3, query, { includeTrash: includeSpamTrash }));
4383
+ let messages = asInputError(() => domain.searchMessages(email2, query, { includeTrash: includeSpamTrash }));
5645
4384
  if (!/\bin:draft\b/i.test(query))
5646
- messages = messages.filter((message2) => !message2.labelIds.includes("DRAFT"));
4385
+ messages = messages.filter((message) => !message.labelIds.includes("DRAFT"));
5647
4386
  if (labelIds2.length) {
5648
- messages = messages.filter((message2) => labelIds2.every((labelId) => message2.labelIds.includes(labelId)));
4387
+ messages = messages.filter((message) => labelIds2.every((labelId) => message.labelIds.includes(labelId)));
5649
4388
  }
5650
4389
  const maxResults = numberQuery(c, "maxResults", 100, 500);
5651
- const snapshot = domain.currentHistoryIdFor(email3);
5652
- const binding = normalizeListBinding("messages.list", email3, { query, includeSpamTrash, labelIds: labelIds2 });
4390
+ const snapshot = domain.currentHistoryIdFor(email2);
4391
+ const binding = normalizeListBinding("messages.list", email2, { query, includeSpamTrash, labelIds: labelIds2 });
5653
4392
  const { page, nextPageToken } = paginate2(messages, {
5654
4393
  maxResults,
5655
4394
  pageToken: c.req.query("pageToken"),
@@ -5658,14 +4397,14 @@ function registerMessageRoutes(app, kit) {
5658
4397
  });
5659
4398
  return {
5660
4399
  body: {
5661
- ...page.length ? { messages: page.map((message2) => ({ id: message2.id, threadId: message2.threadId })) } : {},
4400
+ ...page.length ? { messages: page.map((message) => ({ id: message.id, threadId: message.threadId })) } : {},
5662
4401
  resultSizeEstimate: messages.length,
5663
4402
  ...nextPageToken ? { nextPageToken } : {}
5664
4403
  }
5665
4404
  };
5666
4405
  }));
5667
4406
  app.post(`${BASE2}/batchModify`, kit.write(async (c) => {
5668
- const email3 = emailFromContext(c);
4407
+ const email2 = emailFromContext(c);
5669
4408
  const body = await readJsonObject(c);
5670
4409
  rejectClassification(body);
5671
4410
  const ids = stringArray(body, "ids", 1e3);
@@ -5674,80 +4413,80 @@ function registerMessageRoutes(app, kit) {
5674
4413
  const add = stringArray(body, "addLabelIds");
5675
4414
  const remove = stringArray(body, "removeLabelIds");
5676
4415
  domain.db.transaction(() => {
5677
- for (const id2 of ids)
5678
- domain.modifyMessageLabels(email3, id2, add, remove);
4416
+ for (const id of ids)
4417
+ domain.modifyMessageLabels(email2, id, add, remove);
5679
4418
  }).immediate();
5680
4419
  return { body: {} };
5681
4420
  }));
5682
4421
  app.post(`${BASE2}/batchDelete`, kit.write(async (c) => {
5683
- const email3 = emailFromContext(c);
4422
+ const email2 = emailFromContext(c);
5684
4423
  const body = await readJsonObject(c);
5685
4424
  const ids = stringArray(body, "ids", 1e3);
5686
4425
  if (!ids.length)
5687
4426
  invalidArgument("ids is required");
5688
- domain.batchDeleteMessages(email3, ids);
4427
+ domain.batchDeleteMessages(email2, ids);
5689
4428
  return { status: 204, body: null };
5690
4429
  }));
5691
4430
  const send = kit.write(async (c) => {
5692
- const email3 = emailFromContext(c);
4431
+ const email2 = emailFromContext(c);
5693
4432
  const input = await readMessageWrite(c);
5694
- const result = asInputError(() => domain.sendMessage(email3, input.raw, { threadId: input.threadId }));
5695
- return { body: serializers.message(email3, result.sender, "full") };
4433
+ const result = asInputError(() => domain.sendMessage(email2, input.raw, { threadId: input.threadId }));
4434
+ return { body: serializers.message(email2, result.sender, "full") };
5696
4435
  });
5697
4436
  app.post(`${BASE2}/send`, send);
5698
4437
  app.post(`${UPLOAD2}/send`, send);
5699
4438
  const importMessage = kit.write(async (c) => {
5700
- const email3 = emailFromContext(c);
4439
+ const email2 = emailFromContext(c);
5701
4440
  rejectUnsupportedQuery(c, ["deleted", "processForCalendar"]);
5702
4441
  const source = internalDateSource(c, "dateHeader");
5703
4442
  booleanQuery(c, "neverMarkSpam");
5704
4443
  const input = await readMessageWrite(c);
5705
- const inserted = asInputError(() => domain.insertMessage(email3, input.raw, {
4444
+ const inserted = asInputError(() => domain.insertMessage(email2, input.raw, {
5706
4445
  threadId: input.threadId,
5707
4446
  labels: input.labelIds,
5708
4447
  incoming: true
5709
4448
  }));
5710
- const message2 = domain.applyInternalDateSource(email3, inserted.id, source);
5711
- return { body: serializers.message(email3, message2, "full") };
4449
+ const message = domain.applyInternalDateSource(email2, inserted.id, source);
4450
+ return { body: serializers.message(email2, message, "full") };
5712
4451
  });
5713
4452
  app.post(`${BASE2}/import`, importMessage);
5714
4453
  app.post(`${UPLOAD2}/import`, importMessage);
5715
4454
  const insert = kit.write(async (c) => {
5716
- const email3 = emailFromContext(c);
4455
+ const email2 = emailFromContext(c);
5717
4456
  rejectUnsupportedQuery(c, ["deleted"]);
5718
4457
  const source = internalDateSource(c, "receivedTime");
5719
4458
  const input = await readMessageWrite(c);
5720
- const inserted = asInputError(() => domain.insertMessage(email3, input.raw, {
4459
+ const inserted = asInputError(() => domain.insertMessage(email2, input.raw, {
5721
4460
  threadId: input.threadId,
5722
4461
  labels: input.labelIds
5723
4462
  }));
5724
- const message2 = domain.applyInternalDateSource(email3, inserted.id, source);
5725
- return { body: serializers.message(email3, message2, "full") };
4463
+ const message = domain.applyInternalDateSource(email2, inserted.id, source);
4464
+ return { body: serializers.message(email2, message, "full") };
5726
4465
  });
5727
4466
  app.post(BASE2, insert);
5728
4467
  app.post(UPLOAD2, insert);
5729
4468
  app.get(`${BASE2}/:id`, kit.read((c) => {
5730
- const email3 = emailFromContext(c);
4469
+ const email2 = emailFromContext(c);
5731
4470
  const format = messageFormat(c);
5732
- const message2 = domain.getMessage(email3, routeParam(c, "id"));
5733
- return { body: serializers.message(email3, message2, format, repeatedQuery(c, "metadataHeaders")) };
4471
+ const message = domain.getMessage(email2, routeParam(c, "id"));
4472
+ return { body: serializers.message(email2, message, format, repeatedQuery(c, "metadataHeaders")) };
5734
4473
  }));
5735
4474
  app.post(`${BASE2}/:id/modify`, kit.write(async (c) => {
5736
- const email3 = emailFromContext(c);
4475
+ const email2 = emailFromContext(c);
5737
4476
  const body = await readJsonObject(c);
5738
4477
  rejectClassification(body);
5739
- const message2 = domain.modifyMessageLabels(email3, routeParam(c, "id"), stringArray(body, "addLabelIds"), stringArray(body, "removeLabelIds"));
5740
- return { body: serializers.message(email3, message2, "minimal") };
4478
+ const message = domain.modifyMessageLabels(email2, routeParam(c, "id"), stringArray(body, "addLabelIds"), stringArray(body, "removeLabelIds"));
4479
+ return { body: serializers.message(email2, message, "minimal") };
5741
4480
  }));
5742
4481
  app.post(`${BASE2}/:id/trash`, kit.write((c) => {
5743
- const email3 = emailFromContext(c);
5744
- const message2 = domain.modifyMessageLabels(email3, routeParam(c, "id"), ["TRASH"], ["INBOX"]);
5745
- return { body: serializers.message(email3, message2, "minimal") };
4482
+ const email2 = emailFromContext(c);
4483
+ const message = domain.modifyMessageLabels(email2, routeParam(c, "id"), ["TRASH"], ["INBOX"]);
4484
+ return { body: serializers.message(email2, message, "minimal") };
5746
4485
  }));
5747
4486
  app.post(`${BASE2}/:id/untrash`, kit.write((c) => {
5748
- const email3 = emailFromContext(c);
5749
- const message2 = domain.modifyMessageLabels(email3, routeParam(c, "id"), [], ["TRASH"]);
5750
- return { body: serializers.message(email3, message2, "minimal") };
4487
+ const email2 = emailFromContext(c);
4488
+ const message = domain.modifyMessageLabels(email2, routeParam(c, "id"), [], ["TRASH"]);
4489
+ return { body: serializers.message(email2, message, "minimal") };
5751
4490
  }));
5752
4491
  app.delete(`${BASE2}/:id`, kit.write((c) => {
5753
4492
  domain.deleteMessage(emailFromContext(c), routeParam(c, "id"));
@@ -5778,17 +4517,17 @@ function registerResourceRoutes(app, kit) {
5778
4517
  const { serializers, domain } = kit;
5779
4518
  app.get(`${USERS}/profile`, kit.read((c) => ({ body: domain.profile(emailFromContext(c)) })));
5780
4519
  app.get(`${USERS}/threads`, kit.read((c) => {
5781
- const email3 = emailFromContext(c);
4520
+ const email2 = emailFromContext(c);
5782
4521
  const query = c.req.query("q") ?? "";
5783
4522
  const includeSpamTrash = booleanQuery(c, "includeSpamTrash");
5784
4523
  const labelIds2 = repeatedQuery(c, "labelIds");
5785
- let threads = asInputError(() => domain.searchThreads(email3, query, { includeTrash: includeSpamTrash }));
4524
+ let threads = asInputError(() => domain.searchThreads(email2, query, { includeTrash: includeSpamTrash }));
5786
4525
  if (labelIds2.length) {
5787
4526
  threads = threads.filter((thread) => labelIds2.every((label2) => thread.labelIds.includes(label2)));
5788
4527
  }
5789
4528
  const maxResults = numberQuery(c, "maxResults", 100, 500);
5790
- const snapshot = domain.currentHistoryIdFor(email3);
5791
- const binding = normalizeListBinding("threads.list", email3, { query, includeSpamTrash, labelIds: labelIds2 });
4529
+ const snapshot = domain.currentHistoryIdFor(email2);
4530
+ const binding = normalizeListBinding("threads.list", email2, { query, includeSpamTrash, labelIds: labelIds2 });
5792
4531
  const { page, nextPageToken } = paginate2(threads, {
5793
4532
  maxResults,
5794
4533
  pageToken: c.req.query("pageToken"),
@@ -5800,7 +4539,7 @@ function registerResourceRoutes(app, kit) {
5800
4539
  ...page.length ? {
5801
4540
  threads: page.map((thread) => ({
5802
4541
  id: thread.id,
5803
- historyId: domain.latestThreadHistory(email3, thread.id),
4542
+ historyId: domain.latestThreadHistory(email2, thread.id),
5804
4543
  ...thread.messages.at(-1)?.snippet ? { snippet: thread.messages.at(-1).snippet } : {}
5805
4544
  }))
5806
4545
  } : {},
@@ -5810,28 +4549,28 @@ function registerResourceRoutes(app, kit) {
5810
4549
  };
5811
4550
  }));
5812
4551
  app.get(`${USERS}/threads/:id`, kit.read((c) => {
5813
- const email3 = emailFromContext(c);
4552
+ const email2 = emailFromContext(c);
5814
4553
  const format = messageFormat(c, false);
5815
4554
  return {
5816
- body: serializers.thread(email3, domain.getThread(email3, routeParam(c, "id")), format, repeatedQuery(c, "metadataHeaders"))
4555
+ body: serializers.thread(email2, domain.getThread(email2, routeParam(c, "id")), format, repeatedQuery(c, "metadataHeaders"))
5817
4556
  };
5818
4557
  }));
5819
4558
  app.post(`${USERS}/threads/:id/modify`, kit.write(async (c) => {
5820
- const email3 = emailFromContext(c);
4559
+ const email2 = emailFromContext(c);
5821
4560
  const body = await readJsonObject(c);
5822
- const thread = domain.modifyThreadLabels(email3, routeParam(c, "id"), stringArray(body, "addLabelIds"), stringArray(body, "removeLabelIds"));
5823
- return { body: serializers.thread(email3, thread, "minimal") };
4561
+ const thread = domain.modifyThreadLabels(email2, routeParam(c, "id"), stringArray(body, "addLabelIds"), stringArray(body, "removeLabelIds"));
4562
+ return { body: serializers.thread(email2, thread, "minimal") };
5824
4563
  }));
5825
4564
  app.post(`${USERS}/threads/:id/trash`, kit.write((c) => {
5826
- const email3 = emailFromContext(c);
4565
+ const email2 = emailFromContext(c);
5827
4566
  return {
5828
- body: serializers.thread(email3, domain.modifyThreadLabels(email3, routeParam(c, "id"), ["TRASH"], ["INBOX"]), "minimal")
4567
+ body: serializers.thread(email2, domain.modifyThreadLabels(email2, routeParam(c, "id"), ["TRASH"], ["INBOX"]), "minimal")
5829
4568
  };
5830
4569
  }));
5831
4570
  app.post(`${USERS}/threads/:id/untrash`, kit.write((c) => {
5832
- const email3 = emailFromContext(c);
4571
+ const email2 = emailFromContext(c);
5833
4572
  return {
5834
- body: serializers.thread(email3, domain.modifyThreadLabels(email3, routeParam(c, "id"), [], ["TRASH"]), "minimal")
4573
+ body: serializers.thread(email2, domain.modifyThreadLabels(email2, routeParam(c, "id"), [], ["TRASH"]), "minimal")
5835
4574
  };
5836
4575
  }));
5837
4576
  app.delete(`${USERS}/threads/:id`, kit.write((c) => {
@@ -5841,12 +4580,12 @@ function registerResourceRoutes(app, kit) {
5841
4580
  app.get(`${USERS}/labels`, kit.read((c) => ({ body: { labels: domain.labels(emailFromContext(c)).map(labelSummary) } })));
5842
4581
  app.get(`${USERS}/labels/:id`, kit.read((c) => ({ body: labelDetail(domain.label(emailFromContext(c), routeParam(c, "id"))) })));
5843
4582
  app.post(`${USERS}/labels`, kit.write(async (c) => {
5844
- const email3 = emailFromContext(c);
4583
+ const email2 = emailFromContext(c);
5845
4584
  const body = await readJsonObject(c);
5846
4585
  if (body.type !== void 0 && body.type !== "user")
5847
4586
  invalidArgument("Only user labels can be created");
5848
- const created = domain.createLabel(email3, stringField(body, "name", true), colorInput(body));
5849
- return { body: labelDetail(domain.label(email3, created.id)) };
4587
+ const created = domain.createLabel(email2, stringField(body, "name", true), colorInput(body));
4588
+ return { body: labelDetail(domain.label(email2, created.id)) };
5850
4589
  }));
5851
4590
  app.put(`${USERS}/labels/:id`, kit.write(async (c) => {
5852
4591
  const body = await readJsonObject(c);
@@ -5869,7 +4608,7 @@ function registerResourceRoutes(app, kit) {
5869
4608
  }
5870
4609
  function registerHistory(app, kit) {
5871
4610
  app.get(`${USERS}/history`, kit.read((c) => {
5872
- const email3 = emailFromContext(c);
4611
+ const email2 = emailFromContext(c);
5873
4612
  const startHistoryId = c.req.query("startHistoryId");
5874
4613
  if (!startHistoryId)
5875
4614
  invalidArgument("startHistoryId is required");
@@ -5877,13 +4616,13 @@ function registerHistory(app, kit) {
5877
4616
  const allowed = /* @__PURE__ */ new Set(["messageAdded", "messageDeleted", "labelAdded", "labelRemoved"]);
5878
4617
  if (historyTypes.some((type) => !allowed.has(type)))
5879
4618
  invalidArgument("Invalid historyTypes");
5880
- const result = kit.context.domain.listHistory(email3, startHistoryId, {
4619
+ const result = kit.context.domain.listHistory(email2, startHistoryId, {
5881
4620
  types: historyTypes.length ? historyTypes : void 0
5882
4621
  });
5883
4622
  const labelId = c.req.query("labelId");
5884
4623
  const resources = result.history.filter((event) => !labelId || event.labelIds.includes(labelId)).map(historyResource).filter((item) => item !== null);
5885
4624
  const maxResults = numberQuery(c, "maxResults", 100, 500);
5886
- const binding = normalizeListBinding("history.list", email3, { startHistoryId, historyTypes, labelId });
4625
+ const binding = normalizeListBinding("history.list", email2, { startHistoryId, historyTypes, labelId });
5887
4626
  const { page, nextPageToken } = paginate2(resources, {
5888
4627
  maxResults,
5889
4628
  pageToken: c.req.query("pageToken"),
@@ -6058,4 +4797,4 @@ function createGmailTwinApp(options = {}) {
6058
4797
  });
6059
4798
  }
6060
4799
 
6061
- export { DEFAULT_GMAIL_AGENT_EMAIL, DEFAULT_GMAIL_EMAIL, GMAIL_CHECKS, GmailDomain, GmailError, SEARCH_MAILBOX_MESSAGE_BUDGET, STATE_EXPORT_COLLECTION_CAP, STATE_EXPORT_FULL_MESSAGE_BUDGET, agentPathInboxMailbox, canonicalRaw, capExportRows, composeMime, createGmailTwinApp, decodeGmailRaw, defaultSeedState, encodeGmailRaw, exportGmailState, gmailErrorEnvelope, gmailSeedSchema, gmailStateDelta, gmailTools, gmailTwinDefinition, identityFromSession, loadSeedFromEnv, matchesSearch, migrate, mimeSha256, normalizeSubject, openGmailTwinDatabase, parseMime, parseSearchQuery, parseSeed, projectGmailRecording, registerGmailRoutes, resetDatabase, resolveUserEmail, stripBcc, stripHtmlTags, validateSearchQuery };
4800
+ export { DEFAULT_GMAIL_EMAIL, GmailDomain, STATE_EXPORT_COLLECTION_CAP, STATE_EXPORT_FULL_MESSAGE_BUDGET, canonicalRaw, capExportRows, composeMime, createGmailTwinApp, decodeGmailRaw, encodeGmailRaw, exportGmailState, gmailStateDelta, gmailTools, gmailTwinDefinition, identityFromSession, matchesSearch, migrate, mimeSha256, normalizeSubject, openGmailTwinDatabase, parseMime, projectGmailRecording, registerGmailRoutes, resetDatabase, resolveUserEmail, stripBcc, stripHtmlTags };