Fix silent compose failures: poll actual job status in the compose modal instead of a fire-and-forget dispatch toast; add Groq 429 retry-with-backoff using its own rate-limit-reset header (12k tokens/min tier gets exhausted by gmail_triage alone)

This commit is contained in:
root
2026-07-07 10:49:08 -05:00
parent a4336ebdd6
commit 09f73edb0b
2 changed files with 62 additions and 13 deletions
+32 -6
View File
@@ -4458,6 +4458,7 @@ function outboxCompose() {
<input id="oc-to" class="inp" placeholder="To: email address" type="email">
<input id="oc-subject" class="inp" placeholder="Subject">
<textarea id="oc-body" class="inp" rows="5" style="resize:vertical" placeholder="Describe what to say — AI will draft the full message"></textarea>
<div id="oc-status" style="display:none;font-size:0.65rem;color:var(--text-dim);padding:8px;background:#060a0e;border:1px solid var(--border);border-radius:3px"></div>
</div>
`, async () => {
const to = document.getElementById('oc-to')?.value.trim();
@@ -4465,14 +4466,39 @@ function outboxCompose() {
const body = document.getElementById('oc-body')?.value.trim();
const account = document.getElementById('oc-account')?.value;
if (!to || !body) { toast('Please fill To and message description', 'err'); return; }
const statusEl = document.getElementById('oc-status');
const d = await api('compose_email', {to, subject, body, account});
if (d.job_id) {
toast('Compose job dispatched — Job #' + d.job_id, 'ok');
closeModal();
setTimeout(loadOutbox, 5000);
} else {
toast('Failed: ' + (d.error||'Arc Reactor offline'), 'err');
if (!d.job_id) { toast('Failed: ' + (d.error||'Arc Reactor offline'), 'err'); return; }
// Fixed 2026-07-07: this used to toast "dispatched" and close immediately, with
// no way to ever find out if the job later failed — it just silently vanished.
// Now polls the actual job status until it finishes, so failures are visible.
if (statusEl) { statusEl.style.display = 'block'; statusEl.textContent = 'Drafting — Job #' + d.job_id + '...'; }
document.getElementById('modalSave').disabled = true;
const jobId = d.job_id;
const deadline = Date.now() + 120000;
while (Date.now() < deadline) {
await new Promise(r => setTimeout(r, 3000));
const j = await api('arc_job_get', {id: jobId}).catch(() => null);
if (!j || j.error) continue;
if (j.status === 'done') {
toast('Draft ready: ' + (j.result?.subject || '(no subject)'), 'ok');
closeModal();
loadOutbox();
return;
} else if (j.status === 'failed') {
if (statusEl) {
statusEl.style.color = 'var(--red)';
statusEl.textContent = '✗ Failed: ' + (j.error || 'unknown error');
}
document.getElementById('modalSave').disabled = false;
document.getElementById('modalSave').textContent = 'CLOSE';
document.getElementById('modalSave').onclick = closeModal;
return;
}
if (statusEl) statusEl.textContent = 'Drafting — Job #' + jobId + ' (' + j.status + ')...';
}
if (statusEl) { statusEl.style.color = 'var(--yellow)'; statusEl.textContent = '⚠ Still running in background — check outbox shortly.'; }
document.getElementById('modalSave').disabled = false;
}, 'DISPATCH');
}