Skip to content

feat(messages): add searchAttachments with bounded MIME-parse concurrency (stacked on #145)#149

Open
JordanRO2 wants to merge 2 commits into
TKasperczyk:mainfrom
JordanRO2:feat/search-attachments
Open

feat(messages): add searchAttachments with bounded MIME-parse concurrency (stacked on #145)#149
JordanRO2 wants to merge 2 commits into
TKasperczyk:mainfrom
JordanRO2:feat/search-attachments

Conversation

@JordanRO2

Copy link
Copy Markdown
Contributor

Stacked on #145 — please review/merge that first; the getMessageHeaders / batchGetMessageHeaders commit shown here drops out of this diff once #145 lands. (Independent of #148.)

What

searchAttachments(nameContains, contentType, folderPath, maxResults, scanCap) — find messages whose attachments match a filename substring and/or a Content-Type prefix, scanning a folder (or the inbox of each accessible account when folderPath is omitted). Returns header objects (via the shared msgHdrToHeaderObject from #145) annotated with the matching attachment metadata, newest-first.

Caps: maxResults default 50 / max 200; scanCap default 5000 / max 50000 per folder; candidates pre-filtered on the Attachment flag with a cap*4 buffer; results sliced to cap.

Bounded MIME-parse concurrency (the interesting part)

Resolving attachment metadata requires a MsgHdrToMimeMessage parse per candidate. A naive implementation fires one parse per candidate at once — up to cap*4 (≈800) simultaneous parses, which on IMAP means a burst of network fetches. This PR bounds that:

  • a worker pool of MAX_MIME_CONCURRENCY = 8 pulls candidates from a shared index, so at most 8 parses are ever in flight;
  • each parse is guarded by a one-shot nsITimer timeout so a stuck parse frees its slot instead of wedging the batch;
  • an aggregate deadline stops pulling new work so the call can never exceed the client/bridge request timeout (truncated is set when it trips);
  • the 3-arg MsgHdrToMimeMessage form is used (no allowDownload), so it reads cached MIME without forcing network body downloads.

Tests

test/validation.test.cjs covers the schema; ALL_TOOLS updated (structural test passes). Full suite green, eslint . 0 errors.

Note: the bounded concurrency + timeouts are a deliberate hardening over a straight port. The concurrency/termination logic was reviewed adversarially (worker-pool accounting, timer/callback race, no early-finish/hang/lost-candidate). Happy to tune MAX_MIME_CONCURRENCY / timeout / deadline to your preference.

Header-only reads for agent workflows that need to triage/enrich a result
set without decoding bodies or paying N round-trips.

- getMessageHeaders(messageId, folderPath): returns a flat header object
  (id, subject, author, recipients, ccList, date, tags, isRead, isFlagged,
  threadId, references, inReplyTo, size) via a shared msgHdrToHeaderObject
  extractor; reuses the existing findMessage -> openFolder -> getAccessibleFolder
  access gate (same as getMessage).
- batchGetMessageHeaders(messageIds, folderPath): one folder open + a single
  linear enumeration fallback (O(M), SCAN_CAP 50000) instead of N findMessage
  calls; returns { headers: { id -> header | { error } }, total, failed }.
  Hard cap of 200 enforced in the handler (clear { error }), schema left
  unbounded so the empty-input fast path stays reachable.
- Tests: validation fixtures + ALL_TOOLS entries; full suite 386 pass, lint
  0 errors.
…ncurrency

Find messages whose attachments match a filename substring (nameContains)
and/or a Content-Type prefix (contentType), scanning a folder (or the inbox
of each accessible account when folderPath is omitted). Returns header
objects annotated with the matching attachment metadata.

Reuses the shared msgHdrToHeaderObject extractor and the folder/account
access gate. Caps: maxResults default 50 / max 200; scanCap default 5000 /
max 50000 per folder; candidate prefilter on the Attachment flag with a
cap*4 buffer; results sliced to cap, newest-first.

Hardened over a naive fan-out: the MIME parse step (MsgHdrToMimeMessage)
runs through a bounded worker pool (max 8 concurrent) instead of firing one
parse per candidate at once (up to 800 simultaneous, and network fetches on
IMAP). Each parse is bounded by a one-shot nsITimer timeout, and an
aggregate deadline stops pulling new work so the call can never exceed the
client/bridge request timeout; truncation is signalled via the truncated
flag. The 3-arg MsgHdrToMimeMessage form is used so it never downloads
bodies over the network.

Tests + ALL_TOOLS updated; full suite green, lint 0 errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant