← back to Dw Pitch Followup
pitch: Sent log is now LIVE — merge Gmail Sent (recipient-enriched) with the tool log
f9b6323ef7667979284f1bcde2bcdb880c76af14 · 2026-07-14 11:18:05 -0700 · Steve
georgeSentLetters now fetches each sent letter full message for its `to` (Gmail search omits recipient), bounded to 60 newest, parallel. collectSent merges live+local, dedups by recipient+day, 60s cache. Sent log + per-card emailed chip now show through today (was frozen at the last tool-send). Verified newest = Jul 14.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
M public/index.htmlM server.js
Diff
commit f9b6323ef7667979284f1bcde2bcdb880c76af14
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jul 14 11:18:05 2026 -0700
pitch: Sent log is now LIVE — merge Gmail Sent (recipient-enriched) with the tool log
georgeSentLetters now fetches each sent letter full message for its `to` (Gmail search omits recipient), bounded to 60 newest, parallel. collectSent merges live+local, dedups by recipient+day, 60s cache. Sent log + per-card emailed chip now show through today (was frozen at the last tool-send). Verified newest = Jul 14.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
public/index.html | 2 +-
server.js | 17 +++++++++++------
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/public/index.html b/public/index.html
index f95391d..1a45db8 100644
--- a/public/index.html
+++ b/public/index.html
@@ -527,7 +527,7 @@ async function openSentLog(){
active=null; document.querySelectorAll('.card.active').forEach(c=>c.classList.remove('active'));
document.body.classList.add('log-mode');
document.getElementById('pName').textContent='Sent emails';
- document.getElementById('pSub').textContent='every email sent from this tool';
+ document.getElementById('pSub').textContent='live — follow-up letters from Gmail Sent + this tool (newest first)';
document.getElementById('pCtx').innerHTML='<div class="drow">loading…</div>';
openPanel();
try{
diff --git a/server.js b/server.js
index fe5c1ca..a5ec916 100644
--- a/server.js
+++ b/server.js
@@ -524,17 +524,22 @@ function readSentLog() {
// LIVE view of follow-up letters actually sent from info@ (Gmail Sent), scoped to our
// follow-up subject — so the Sent log + per-card chips reflect reality even for sends made
// outside this tool instance (the local jsonl only sees sends fired from THIS process).
-async function georgeSentLetters(maxResults = 200) {
+async function georgeSentLetters(maxResults = 40) {
try {
const q = encodeURIComponent('in:sent subject:(Designer Wallcoverings samples)');
const r = await fetch(`${GEORGE}/api/search?q=${q}&account=info&maxResults=${maxResults}`, {
headers: { Authorization: GEORGE_AUTH }, signal: AbortSignal.timeout(10000),
});
if (!r.ok) return [];
- const j = await r.json();
- return (j.messages || []).map((m) => {
- const toM = String(m.to || m.recipient || m.headers?.to || '').match(/[\w.+-]+@[\w.-]+\.\w+/);
- return { ts: m.date || '', to: toM ? toM[0].toLowerCase() : '', subject: m.subject || '', messageId: m.id || '', inThread: false, live: true };
+ const ids = ((await r.json()).messages || []).map((m) => m.id).filter(Boolean).slice(0, maxResults);
+ // Gmail search omits the recipient; fetch the full message (has `to`) for each — parallel, bounded.
+ const msgs = await Promise.all(ids.map((id) =>
+ fetch(`${GEORGE}/api/messages/${id}?account=info`, { headers: { Authorization: GEORGE_AUTH }, signal: AbortSignal.timeout(8000) })
+ .then((x) => (x.ok ? x.json() : null)).catch(() => null)));
+ return msgs.filter(Boolean).map((m) => {
+ const toM = String(m.to || '').match(/[\w.+-]+@[\w.-]+\.\w+/);
+ let ts = ''; try { ts = m.date ? new Date(m.date).toISOString() : ''; } catch { ts = ''; }
+ return { ts, to: toM ? toM[0].toLowerCase() : '', subject: m.subject || '', messageId: m.id || '', inThread: /^\s*re:/i.test(m.subject || ''), live: true };
}).filter((x) => x.ts && x.to);
} catch { return []; }
}
@@ -544,7 +549,7 @@ let _sentCache = { at: 0, data: null };
async function collectSent() {
if (_sentCache.data && Date.now() - _sentCache.at < 60000) return _sentCache.data;
const local = readSentLog().map((o) => ({ ...o, to: String(o.to || '').toLowerCase() }));
- const live = await georgeSentLetters(200);
+ const live = await georgeSentLetters(60); // 60 newest from Gmail; the local log covers history
const key = (x) => `${x.to}|${String(x.ts).slice(0, 10)}`;
const map = new Map();
for (const x of live) map.set(key(x), x);
← 6c560f3 pitch: FIX critical George 401 — resolve GEORGE_AUTH from se
·
back to Dw Pitch Followup
·
pitch: list2 letter follows up on the specific QUOTE (number c6994a4 →