personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

convey/home: migrate pulse refreshers to wave 0 primitives (wave 2)

Move the vitals and narrative refreshers to apiJson.
Preserve last-known pulse content and surface refresh failures beside each panel.

+34 -6
+34 -6
apps/home/workspace.html
··· 1579 1579 } catch(e) {} 1580 1580 }; 1581 1581 1582 + function clearPulseRefreshError(containerId) { 1583 + const container = document.getElementById(containerId); 1584 + const siblingError = container?.nextElementSibling; 1585 + if (siblingError && siblingError.classList.contains('surface-state-refresh-error')) { 1586 + siblingError.remove(); 1587 + } 1588 + } 1589 + 1582 1590 function refreshVitals() { 1583 - fetch('/app/home/api/pulse') 1584 - .then(r => r.json()) 1591 + window.apiJson('/app/home/api/pulse') 1585 1592 .then(data => { 1586 1593 const el = document.getElementById('pulse-vitals'); 1587 1594 if (!el) return; 1595 + if (typeof data.capture_status !== 'string') { 1596 + throw malformedHomeResponse('/app/home/api/pulse', 'Malformed pulse response'); 1597 + } 1598 + clearPulseRefreshError('pulse-vitals'); 1588 1599 let dotClass = data.capture_status === 'active' ? 'active' : (data.capture_status === 'stale' ? 'stale' : (data.capture_status === 'offline' ? 'offline' : 'unknown')); 1589 1600 let displayStatus = data.capture_status === 'no_observers' ? 'no observers' : data.capture_status; 1590 1601 let html = '<div class="pulse-vitals-item"><span class="pulse-vitals-dot ' + dotClass + '" aria-hidden="true"></span><span>capture ' + esc(displayStatus) + '</span></div>'; ··· 1605 1616 html += '<a href="/app/health">health →</a>'; 1606 1617 el.innerHTML = html; 1607 1618 }) 1608 - .catch(function(err) { window.logError(err, { context: 'home: refreshVitals failed' }); }); 1619 + .catch(function(err) { 1620 + window.logError(err, { context: 'home: refreshVitals failed' }); 1621 + window.SurfaceState.replaceLoading('pulse-vitals', window.SurfaceState.errorCard({ 1622 + heading: "Couldn't refresh vitals — showing last known state.", 1623 + desc: 'Reload to try again.', 1624 + serverMessage: err.serverMessage, 1625 + })); 1626 + }); 1609 1627 } 1610 1628 1611 1629 function refreshNarrative() { 1612 - fetch('/app/home/api/pulse') 1613 - .then(r => r.json()) 1630 + window.apiJson('/app/home/api/pulse') 1614 1631 .then(data => { 1632 + if (!data || typeof data !== 'object' || !Object.prototype.hasOwnProperty.call(data, 'narrative_content')) { 1633 + throw malformedHomeResponse('/app/home/api/pulse', 'Malformed pulse response'); 1634 + } 1635 + clearPulseRefreshError('pulse-narrative'); 1615 1636 const el = document.getElementById('pulse-narrative-content'); 1616 1637 if (el && data.narrative_content) { 1617 1638 el.innerHTML = window.AppServices.renderMarkdown(data.narrative_content); ··· 1627 1648 } 1628 1649 } 1629 1650 }) 1630 - .catch(function(err) { window.logError(err, { context: 'home: refreshNarrative failed' }); }); 1651 + .catch(function(err) { 1652 + window.logError(err, { context: 'home: refreshNarrative failed' }); 1653 + window.SurfaceState.replaceLoading('pulse-narrative', window.SurfaceState.errorCard({ 1654 + heading: "Couldn't refresh narrative — showing last known state.", 1655 + desc: 'Reload to try again.', 1656 + serverMessage: err.serverMessage, 1657 + })); 1658 + }); 1631 1659 } 1632 1660 1633 1661 function esc(s) {