personal memory agent
0
fork

Configure Feed

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

web-ui: sweep surveillance vocab + add trinity onboarding card

Phase B canon sweep applied to the four highest-traffic web UI surfaces:
- convey/templates/init.html: trinity onboarding card (observers / sol /
your journal) inserted before section-password; observer-empty copy
rewritten to canon vocabulary.
- convey/templates/status_pane.html: heading and JS status strings shift
from "capture" → "observer status" / "observer".
- apps/home/workspace.html: pulse-vitals dot label, narrative welcome
copy, diagnostic link, and JS pulse-string builder all rewritten;
narrative names "observers" and "sol curates" per canon.
- apps/settings/workspace.html: nav optgroup + sidebar group rename to
"observation"; terminal-section heading, toggle, and helper text shift
to "terminal observer" / "take in" / "poll interval"; talents segment
note drops "recording"; cleanup copy says "screen-frame originals";
JS-rendered help text uses "poll".

Code identifiers preserved verbatim (DOM ids, JS variables, Jinja
context vars, config keys, API field paths, function names). Plaud
"recordings" and audio-engineering "silent/noisy recordings" terms
retained as third-party / domain-precision vocabulary.

Tier-3 chrome lands at neutral vocabulary ("observer status",
"observation"); branded trinity labels appear only in the new
onboarding card and the home pulse narrative paragraph.

Continues the system-anatomy canon rollout (Phase B) following:
- ddec60d5 (lode kewybdze)
- 929448de (lode 4gzemhrg)
- b1e344b5 (lode d4k4c3cy)
- d7ce7deb (lode htbdnmcy)

Founder option (c) — ongoing chrome surfacing of the trinity — remains
deferred. Surveillance-vocab sweep across less-trafficked surfaces and
the API field rename `capture_status` → `observer_status` are tracked
as future work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+34 -23
+4 -4
apps/home/workspace.html
··· 1018 1018 {% set dot_class = 'active' if capture_status == 'active' else ('stale' if capture_status == 'stale' else ('offline' if capture_status == 'offline' else 'unknown')) %} 1019 1019 <div class="pulse-vitals-item"> 1020 1020 <span class="pulse-vitals-dot {{ dot_class }}" aria-hidden="true"></span> 1021 - <span>capture {{ 'no observers' if capture_status == 'no_observers' else capture_status }}</span> 1021 + <span>observer {{ 'no observers' if capture_status == 'no_observers' else capture_status }}</span> 1022 1022 </div> 1023 1023 {% if segment_count > 0 %} 1024 1024 <div class="pulse-vitals-sep"></div> ··· 1074 1074 {% if show_welcome %} 1075 1075 <div class="pulse-welcome"> 1076 1076 <h2>welcome to your home page</h2> 1077 - <p>this is where your day comes together — narrative summaries, calendar events, tasks, routines, and the people in your network. as solstone captures and processes your day, sections will appear here automatically.</p> 1077 + <p>this is where your day comes together — narrative summaries, calendar events, tasks, routines, and the people in your network. as observers experience your day along with you and sol curates, sections will appear here automatically.</p> 1078 1078 <a href="/app/health">check system health →</a> 1079 1079 </div> 1080 1080 {% endif %} ··· 1153 1153 {% if narrative_content is none and segment_count == 0 and not show_welcome %} 1154 1154 <div class="pulse-empty-state"> 1155 1155 <h2 class="pulse-section-header">today's flow</h2> 1156 - <div class="pulse-empty-message">no narrative yet — one will appear after solstone processes some audio. <a href="/app/health">check capture status →</a></div> 1156 + <div class="pulse-empty-message">no narrative yet — one will appear after solstone processes some audio. <a href="/app/health">check observer status →</a></div> 1157 1157 </div> 1158 1158 {% endif %} 1159 1159 ··· 1603 1603 clearPulseRefreshError('pulse-vitals'); 1604 1604 let dotClass = data.capture_status === 'active' ? 'active' : (data.capture_status === 'stale' ? 'stale' : (data.capture_status === 'offline' ? 'offline' : 'unknown')); 1605 1605 let displayStatus = data.capture_status === 'no_observers' ? 'no observers' : data.capture_status; 1606 - let html = '<div class="pulse-vitals-item"><span class="pulse-vitals-dot ' + dotClass + '" aria-hidden="true"></span><span>capture ' + esc(displayStatus) + '</span></div>'; 1606 + let html = '<div class="pulse-vitals-item"><span class="pulse-vitals-dot ' + dotClass + '" aria-hidden="true"></span><span>observer ' + esc(displayStatus) + '</span></div>'; 1607 1607 if (data.segment_count > 0) { 1608 1608 html += '<div class="pulse-vitals-sep"></div><div class="pulse-vitals-item">' + data.segment_count + ' segment' + (data.segment_count !== 1 ? 's' : ''); 1609 1609 if (data.duration_minutes > 0) html += ' · ' + data.duration_minutes + 'm';
+12 -12
apps/settings/workspace.html
··· 1957 1957 <option value="transcription">transcription</option> 1958 1958 <option value="insights">insights</option> 1959 1959 </optgroup> 1960 - <optgroup label="capture"> 1960 + <optgroup label="observation"> 1961 1961 <option value="observer">observer</option> 1962 1962 <option value="vision">vision</option> 1963 1963 </optgroup> ··· 1993 1993 <button class="settings-nav-item" data-section="insights" id="tab-insights" role="tab" aria-selected="false" aria-controls="section-insights" tabindex="-1">insights</button> 1994 1994 </div> 1995 1995 <div class="settings-nav-group"> 1996 - <div class="settings-nav-label">capture</div> 1996 + <div class="settings-nav-label">observation</div> 1997 1997 <button class="settings-nav-item" data-section="observer" id="tab-observer" role="tab" aria-selected="false" aria-controls="section-observer" tabindex="-1">observer</button> 1998 1998 <button class="settings-nav-item" data-section="vision" id="tab-vision" role="tab" aria-selected="false" aria-controls="section-vision" tabindex="-1">vision</button> 1999 1999 </div> ··· 2463 2463 <!-- Observer Section --> 2464 2464 <section class="settings-section" id="section-observer" role="tabpanel" aria-labelledby="tab-observer"> 2465 2465 <h2>observer</h2> 2466 - <p class="settings-section-desc">Configure capture behavior for the observer service.</p> 2466 + <p class="settings-section-desc">Configure how the observer service operates.</p> 2467 2467 <div id="observerLoadState" class="settings-load-state" aria-live="polite"></div> 2468 2468 2469 2469 <form class="settings-form" onsubmit="return false;"> 2470 2470 2471 - <h3 class="settings-subsection-title">terminal capture</h3> 2471 + <h3 class="settings-subsection-title">terminal observer</h3> 2472 2472 <p class="settings-subsection-desc"> 2473 - When your screen is idle, the observer can capture terminal content from active tmux sessions. 2473 + When your screen is idle, the observer can take in terminal content from active tmux sessions. 2474 2474 </p> 2475 2475 2476 2476 <div class="settings-field"> 2477 - <label for="field-tmux-enabled">enable terminal capture</label> 2477 + <label for="field-tmux-enabled">enable terminal observation</label> 2478 2478 <div style="display: flex; align-items: center; gap: 1em;"> 2479 2479 <label class="toggle-switch toggle-positive"> 2480 2480 <input type="checkbox" id="field-tmux-enabled"> 2481 2481 <span class="slider"></span> 2482 2482 </label> 2483 - <span style="color: #666; font-size: 0.9em;">Capture tmux sessions when screen is idle</span> 2483 + <span style="color: #666; font-size: 0.9em;">Take in tmux sessions when screen is idle</span> 2484 2484 </div> 2485 2485 </div> 2486 2486 2487 2487 <div class="settings-field" id="tmuxCaptureIntervalField"> 2488 - <label for="field-tmux-capture-interval">capture interval (seconds)</label> 2488 + <label for="field-tmux-capture-interval">poll interval (seconds)</label> 2489 2489 <input type="number" id="field-tmux-capture-interval" style="max-width: 100px;"> 2490 - <small id="tmuxCaptureIntervalHelp">How often to capture terminal content</small> 2490 + <small id="tmuxCaptureIntervalHelp">How often to poll terminal content</small> 2491 2491 </div> 2492 2492 2493 2493 <p style="color: #888; font-size: 0.85em; margin-top: 1.5em; padding: 0.75em; background: #f8f9fa; border-radius: 4px;"> ··· 2554 2554 <span class="insights-group-toggle">&#9660;</span> 2555 2555 </div> 2556 2556 <div class="insights-group-body" id="segmentInsightsBody"> 2557 - <p style="color:#666;font-size:0.85em;margin:0 0 0.75em 0">Run on each recording segment (5 minutes of audio/screen).</p> 2557 + <p style="color:#666;font-size:0.85em;margin:0 0 0.75em 0">Run on each segment (5 minutes of audio/screen).</p> 2558 2558 <div id="segmentInsightsList">Loading...</div> 2559 2559 </div> 2560 2560 </div> ··· 3002 3002 <div class="color-modal-content" style="max-width: 480px;"> 3003 3003 <span class="color-close" id="cleanupModalClose">&times;</span> 3004 3004 <h3 id="cleanupHeading">clean up raw media</h3> 3005 - <p style="color: #666; font-size: 0.9em; margin-bottom: 1em;">Delete raw media files (audio, video, screen captures) from segments older than the specified number of days.</p> 3005 + <p style="color: #666; font-size: 0.9em; margin-bottom: 1em;">Delete raw media files (audio, video, screen-frame originals) from segments older than the specified number of days.</p> 3006 3006 3007 3007 <div class="settings-field" style="margin-bottom: 1em;"> 3008 3008 <label for="cleanupDaysInput">older than</label> ··· 5009 5009 // Update helper text with default value 5010 5010 const helpText = document.getElementById('tmuxCaptureIntervalHelp'); 5011 5011 if (helpText) { 5012 - helpText.textContent = `How often to capture terminal content (default: ${defaults.capture_interval}s)`; 5012 + helpText.textContent = `How often to poll terminal content (default: ${defaults.capture_interval}s)`; 5013 5013 } 5014 5014 5015 5015 updateTmuxCaptureIntervalVisibility();
+12 -1
convey/templates/init.html
··· 109 109 <h1>set up solstone</h1> 110 110 <p class="product-tagline">your private agentic partner — sol</p> 111 111 112 + <section class="init-section" id="section-trinity"> 113 + <h3>welcome</h3> 114 + <p>solstone is three things working together:</p> 115 + <ul style="list-style: none; padding-left: 0; margin: 0.5em 0 1em 0;"> 116 + <li style="margin-bottom: 0.5em;"><strong>observers</strong> — experience your day along with you, on each device</li> 117 + <li style="margin-bottom: 0.5em;"><strong>sol</strong> — the agent that curates your memories</li> 118 + <li style="margin-bottom: 0.5em;"><strong>your journal</strong> — the folder on your machine that holds everything</li> 119 + </ul> 120 + <p>install gets you all three. the steps below set up sol; you'll add observers in step 4.</p> 121 + </section> 122 + 112 123 <section class="init-section" id="section-password"> 113 124 <h3>1. set a password</h3> 114 125 <p class="section-hint">{{ init_password_hint | safe }}</p> ··· 157 168 <div id="observer-status"> 158 169 <p class="section-hint" id="observer-label" style="display: none;">connected observers</p> 159 170 <div id="observer-list"></div> 160 - <p class="observer-empty" id="observer-empty">no observer connected yet — <a href="https://solstone.app/observers" tabindex="-1">set up an observer</a> to start capturing</p> 171 + <p class="observer-empty" id="observer-empty">no observer connected yet — <a href="https://solstone.app/observers" tabindex="-1">set up an observer</a> to start observing along with you</p> 161 172 <div id="observer-error" class="error-message" hidden></div> 162 173 </div> 163 174 </section>
+6 -6
convey/templates/status_pane.html
··· 15 15 </details> 16 16 17 17 <div id="capture-status-section" style="margin-top: 12px; display: none;"> 18 - <h4 style="margin: 0 0 4px 0; font-size: 13px; font-weight: 600;">capture</h4> 18 + <h4 style="margin: 0 0 4px 0; font-size: 13px; font-weight: 600;">observer status</h4> 19 19 <span id="capture-status-text" style="font-size: 13px;"></span> 20 20 </div> 21 21 ··· 171 171 } 172 172 } 173 173 174 - // Update composite icon if we have capture data 174 + // Update composite icon if we have observer data 175 175 if (_lastCaptureStatus !== null) updateCompositeIcon(); 176 176 177 177 // Update notification history ··· 204 204 text.style.color = '#10b981'; 205 205 } else if (status === 'stale') { 206 206 const names = (capture.observers || []).filter(o => o.status === 'stale').map(o => o.name).join(', '); 207 - text.textContent = 'capture ' + displayStatus + (names ? ' — ' + names : ''); 207 + text.textContent = 'observer ' + displayStatus + (names ? ' — ' + names : ''); 208 208 text.style.color = '#f59e0b'; 209 209 return; 210 210 } else if (status === 'offline') { ··· 212 212 } else { 213 213 text.style.color = '#9ca3af'; 214 214 } 215 - text.textContent = 'capture ' + displayStatus; 215 + text.textContent = 'observer ' + displayStatus; 216 216 } 217 217 218 218 function renderVersionSection(version) { ··· 239 239 const metrics = window.appEvents?.getMetrics?.(); 240 240 const wsState = metrics?.state || 'disconnected'; 241 241 242 - // Map capture status to severity: active=0, no_observers=0, stale=1, offline=2 242 + // Map observer status to severity: active=0, no_observers=0, stale=1, offline=2 243 243 // Map WS state to severity: connected=0, connecting=1, disconnected=2 244 244 const captureSeverity = { active: 0, no_observers: 0, stale: 1, offline: 2 }; 245 245 const wsSeverity = { connected: 0, connecting: 1, disconnected: 2 }; ··· 247 247 const cs = captureSeverity[_lastCaptureStatus] ?? 0; 248 248 const ws = wsSeverity[wsState] ?? 2; 249 249 250 - // Only override icon if capture makes it worse than WS alone 250 + // Only override icon if observer status makes it worse than WS alone 251 251 if (cs <= ws) return; 252 252 253 253 const worst = Math.max(cs, ws);