Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@
## 2026-06-29 - Optimize D1 Queries by Batching
**Learning:** Found N+1 query pattern on the hub stats page (`hub/src/routes/admin/stats.ts`) when iterating over the list of inbound peers to query `events_pulled_24h` for each peer sequentially using `.first()`. This introduces significant roundtrip latency and resource overhead as the number of peers grows. D1 supports batching queries, which can collapse N queries into a single HTTP roundtrip. D1's returned `batch` types can be peculiar, particularly when running against tests using Miniflare or test shims where row result structure might differ across platforms.
**Action:** Replaced sequential queries in `Promise.all` with `db.batch()` to combine the multiple `db.prepare(...).bind(...)` calls into a single execution, and normalized the output structure parsing. This significantly reduces N+1 overhead and improves performance scaling as the peer count increases.
## 2026-07-25 - Avoid N+1 requests in agent thread loading
**Learning:** Found N+1 query pattern on the `workers/agent/index.ts` file, where `threadEmails.map` iterated to call `stub.getEmail` sequentially which adds overhead to agent context loading.
**Action:** Replaced loop inside `workers/agent/index.ts` with a single call to `getFullThread(stub, emailData.threadId)`.
26 changes: 4 additions & 22 deletions workers/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
getMailboxStub,
stripHtmlToText,
textToHtml,
getFullThread,
} from "../lib/email-helpers";
import { resolveMailboxSettings } from "../lib/mailbox-settings";
import type { EmailFull, EmailMetadata } from "../lib/schemas";
Expand Down Expand Up @@ -433,28 +434,9 @@ export class EmailAgent extends AIChatAgent<any> {
}

// Load thread for conversation context
const threadEmails = (await stub.getEmails({
thread_id: emailData.threadId,
})) as EmailMetadata[];
if (threadEmails.length > 1) {
const fullThread = await Promise.all(
threadEmails.map(async (e) => {
const full = (await stub.getEmail(e.id)) as EmailFull | null;
const text = full?.body ? stripHtmlToText(full.body) : "";
return {
id: e.id,
sender: e.sender,
recipient: e.recipient,
subject: e.subject,
date: e.date,
folder_id: e.folder_id,
body_text: text,
};
}),
);
// ⚑ Bolt: Optimize sorting by replacing `new Date(b.date).getTime()` with `Date.parse(b.date)`
fullThread.sort((a, b) => Date.parse(a.date) - Date.parse(b.date));
threadContext = fullThread
const threadEmails = await getFullThread(stub, emailData.threadId);
if (threadEmails.message_count > 1) {
threadContext = threadEmails.messages
.map(
(e) =>
`[${e.date}] ${e.sender} β†’ ${e.recipient} (${e.folder_id}): ${e.body_text.substring(0, 500)}`,
Expand Down
Loading