feat: opt-in state.db sync for /insights visibility (#92)
WebUI sessions were invisible to 'hermes /insights' because the WebUI bypasses the gateway and calls AIAgent.run_conversation() directly, never writing to state.db. New 'Sync usage to /insights' setting (default: off) that mirrors WebUI session metadata (tokens, cost, model, title) into state.db after each turn. Uses absolute token counts to avoid double-counting. Components: - api/state_sync.py: bridge module with sync_session_start() and sync_session_usage(). Uses ensure_session() (idempotent) and update_token_counts(absolute=True). All wrapped in try/except. - api/config.py: new 'sync_to_insights' boolean setting - api/streaming.py: calls sync_session_usage() after s.save() - api/routes.py: same for the non-streaming chat path - Settings UI: checkbox toggle with description Default off because: - Writing to state.db while CLI/gateway also writes could cause WAL lock contention on busy systems - Some users may not want WebUI sessions in /insights stats Closes #92 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -343,6 +343,13 @@
|
||||
</label>
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:4px">Merges sessions from the Hermes CLI (state.db) into the session list. Click a CLI session to import it and continue the conversation.</div>
|
||||
</div>
|
||||
<div class="settings-field">
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
|
||||
<input type="checkbox" id="settingsSyncInsights" style="width:15px;height:15px;accent-color:var(--accent)">
|
||||
Sync usage to /insights
|
||||
</label>
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:4px">Mirrors WebUI token usage to state.db so <code>hermes /insights</code> includes browser session data. Off by default.</div>
|
||||
</div>
|
||||
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
|
||||
<label for="settingsPassword">Access Password</label>
|
||||
<div style="font-size:11px;color:var(--muted);margin-bottom:6px">Enter a new password to set or change it. Leave blank to keep current setting.</div>
|
||||
|
||||
@@ -962,6 +962,8 @@ async function loadSettingsPanel(){
|
||||
if(showUsageCb) showUsageCb.checked=!!settings.show_token_usage;
|
||||
const showCliCb=$('settingsShowCliSessions');
|
||||
if(showCliCb) showCliCb.checked=!!settings.show_cli_sessions;
|
||||
const syncCb=$('settingsSyncInsights');
|
||||
if(syncCb) syncCb.checked=!!settings.sync_to_insights;
|
||||
// Password field: always blank (we don't send hash back)
|
||||
const pwField=$('settingsPassword');
|
||||
if(pwField) pwField.value='';
|
||||
@@ -992,6 +994,7 @@ async function saveSettings(){
|
||||
if(sendKey) body.send_key=sendKey;
|
||||
body.show_token_usage=showTokenUsage;
|
||||
body.show_cli_sessions=showCliSessions;
|
||||
body.sync_to_insights=!!($('settingsSyncInsights')||{}).checked;
|
||||
// Password: only act if the field has content; blank = leave auth unchanged
|
||||
if(pw && pw.trim()){
|
||||
try{
|
||||
|
||||
Reference in New Issue
Block a user