diff --git a/static/js/chat.js b/static/js/chat.js index 65e4d17de..12df53973 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -628,8 +628,13 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer } } let _userMsgEl = null; + // Capture which mode the user picked at send time so the message + // header can show "Chat" or "Agent" next to the timestamp. + const _sendMode = (toggleState.mode || 'chat') === 'agent' ? 'agent' : 'chat'; if (!skipBubble) { - _userMsgEl = addMessage('user', userDisplay, null, _pendingAttachInfo ? { attachments: _pendingAttachInfo } : null); + const _userMeta = { mode: _sendMode }; + if (_pendingAttachInfo) _userMeta.attachments = _pendingAttachInfo; + _userMsgEl = addMessage('user', userDisplay, null, _userMeta); } messageInput.value = ''; messageInput.style.height = ''; @@ -3423,6 +3428,9 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer if (holder.parentNode) holder.remove(); const model = meta && meta.model; const meta_ = metricsData ? Object.assign({ model }, metricsData) : { model }; + // Carry the send-time mode through so the assistant header gets + // the same Chat/Agent tag next to its timestamp. + meta_.mode = (toggleState.mode || 'chat') === 'agent' ? 'agent' : 'chat'; chatRenderer.addMessage('assistant', roundText, model, meta_); uiModule.scrollHistory(); return true; diff --git a/static/js/chatRenderer.js b/static/js/chatRenderer.js index 9a5c6f78b..2994fef9d 100644 --- a/static/js/chatRenderer.js +++ b/static/js/chatRenderer.js @@ -832,8 +832,10 @@ export function updateSessionCostUI() { /** Create a timestamp span for role labels. * Pass an ISO string / Date / epoch-ms to render the message's own time - * (used when replaying history). Falls back to "now" when no value is given. */ -export function roleTimestamp(when) { + * (used when replaying history). Falls back to "now" when no value is given. + * Optionally pass `mode` ('chat' | 'agent') to append a small badge so the + * reader can tell at a glance which path the message went through. */ +export function roleTimestamp(when, mode) { const ts = document.createElement('span'); ts.className = 'role-timestamp'; let d; @@ -844,6 +846,12 @@ export function roleTimestamp(when) { if (isNaN(d.getTime())) d = new Date(); ts.textContent = d.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); ts.title = d.toLocaleString(); + if (mode === 'agent' || mode === 'chat') { + const tag = document.createElement('span'); + tag.className = 'role-mode-tag role-mode-' + mode; + tag.textContent = mode === 'agent' ? 'Agent' : 'Chat'; + ts.appendChild(tag); + } return ts; } @@ -2005,7 +2013,7 @@ export function addMessage(role, content, modelName, metadata) { roleEl.title = pair.requestedModel + ' -> ' + contModel; } applyModelColor(roleEl, contModel); - if (r === 0) roleEl.appendChild(roleTimestamp(metadata?.timestamp)); + if (r === 0) roleEl.appendChild(roleTimestamp(metadata?.timestamp, metadata?.mode)); wrap.appendChild(roleEl); const body = document.createElement('div'); body.className = 'body'; @@ -2165,7 +2173,7 @@ export function addMessage(role, content, modelName, metadata) { r.title = replyModels.requestedModel + ' -> ' + resolvedModel; } if (!isSlash && !isCompacted) applyModelColor(r, resolvedModel); - r.appendChild(roleTimestamp(metadata?.timestamp)); + r.appendChild(roleTimestamp(metadata?.timestamp, metadata?.mode)); } const b = document.createElement('div'); @@ -2410,7 +2418,7 @@ export function addMessage(role, content, modelName, metadata) { if (metadata) displayMetrics(wrap, metadata); } else { // Add timestamp to user header (like AI messages) - r.appendChild(roleTimestamp(metadata?.timestamp)); + r.appendChild(roleTimestamp(metadata?.timestamp, metadata?.mode)); wrap.appendChild(createUserMsgFooter(wrap)); } diff --git a/static/style.css b/static/style.css index 6e8b03040..8e34e0b7e 100644 --- a/static/style.css +++ b/static/style.css @@ -3247,6 +3247,26 @@ body.bg-pattern-sparkles { .role-timestamp { font-size:0.7rem; color:var(--color-muted-alt); font-weight:normal; margin-left:6px; } + /* Mode tag (Chat / Agent) sits right after the timestamp so users + can tell at a glance which path a message took. Subtle pill — + no accent background, just text + thin border. */ + .role-mode-tag { + display: inline-block; + margin-left: 6px; + padding: 0 5px; + font-size: 0.62rem; + line-height: 1.6; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.4px; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--fg) 18%, transparent); + opacity: 0.7; + } + .role-mode-tag.role-mode-agent { + color: var(--accent, var(--red)); + border-color: color-mix(in srgb, var(--accent, var(--red)) 35%, transparent); + } .msg-footer { display:flex; align-items:center; gap:6px; flex-wrap: wrap;