## CHANGES

- Bumped Codex session cache version to refresh improved previews! 🚀
- Skips system/environment context tags when building first-message previews! 
- Adds robust system-context detection for cleaner session list summaries! 🧹
- Improves first user-message capture, avoiding noisy XML-like metadata! 🎯
- Refines CWD extraction flow, reusing parsed text more efficiently! 
- Resets aggregate stats when agentId changes, not just cwd! 🔄
- Simplifies total token display layout with cleaner inline computation! 🧠
- Shows tokens and context percentage together for faster readability! 👀
- Enhances tab naming across Claude, OpenCode, and Codex session IDs! 🏷️
- Adds smart session-id truncation fallbacks for consistent tab labels! 
This commit is contained in:
Pedram Amini
2025-12-21 17:08:31 -06:00
parent b8908defa8
commit 9eef7b7728
3 changed files with 71 additions and 40 deletions

View File

@@ -52,7 +52,7 @@ function getCodexSessionsDir(): string {
const CODEX_SESSIONS_DIR = getCodexSessionsDir();
const CODEX_SESSION_CACHE_VERSION = 1;
const CODEX_SESSION_CACHE_VERSION = 2; // Bumped: skip system context in firstMessage preview
const CODEX_SESSION_CACHE_FILENAME = 'codex-sessions-cache.json';
/**
@@ -135,6 +135,19 @@ function extractTextFromContent(content: CodexMessageContent[] | undefined): str
return textParts.join(' ');
}
/**
* Check if text is a system/environment context message that should be skipped for preview
*/
function isSystemContextMessage(text: string): boolean {
if (!text) return false;
const trimmed = text.trim();
// Skip messages that start with environment/system context XML tags
return trimmed.startsWith('<environment_context>') ||
trimmed.startsWith('<cwd>') ||
trimmed.startsWith('<system>') ||
trimmed.startsWith('<approval_policy>');
}
function extractCwdFromText(text: string): string | null {
const match = text.match(/<cwd>([^<]+)<\/cwd>/i);
return match ? match[1].trim() : null;
@@ -273,19 +286,19 @@ async function parseSessionFile(
if (entry.type === 'message') {
if (entry.role === 'user') {
userMessageCount++;
// Capture first user message as fallback preview
if (!firstUserMessage && entry.content) {
// Extract text and check for system context
if (entry.content) {
const text = extractTextFromContent(entry.content);
if (text.trim()) {
// Capture first user message as fallback preview (skip system context)
if (!firstUserMessage && text.trim() && !isSystemContextMessage(text)) {
firstUserMessage = text;
}
}
// Fallback: extract cwd from message content if not in session_meta
if (!sessionProjectPath && entry.content) {
const text = extractTextFromContent(entry.content);
const cwd = extractCwdFromText(text);
if (cwd) {
sessionProjectPath = cwd;
// Fallback: extract cwd from message content if not in session_meta
if (!sessionProjectPath) {
const cwd = extractCwdFromText(text);
if (cwd) {
sessionProjectPath = cwd;
}
}
}
} else if (entry.role === 'assistant') {
@@ -304,19 +317,19 @@ async function parseSessionFile(
if (entry.type === 'response_item' && entry.payload?.type === 'message') {
if (entry.payload.role === 'user') {
userMessageCount++;
// Capture first user message as fallback preview
if (!firstUserMessage && entry.payload.content) {
// Extract text and check for system context
if (entry.payload.content) {
const text = extractTextFromContent(entry.payload.content);
if (text.trim()) {
// Capture first user message as fallback preview (skip system context)
if (!firstUserMessage && text.trim() && !isSystemContextMessage(text)) {
firstUserMessage = text;
}
}
// Fallback: extract cwd from message content if not in session_meta
if (!sessionProjectPath && entry.payload.content) {
const text = extractTextFromContent(entry.payload.content);
const cwd = extractCwdFromText(text);
if (cwd) {
sessionProjectPath = cwd;
// Fallback: extract cwd from message content if not in session_meta
if (!sessionProjectPath) {
const cwd = extractCwdFromText(text);
if (cwd) {
sessionProjectPath = cwd;
}
}
}
} else if (entry.payload.role === 'assistant') {

View File

@@ -176,10 +176,10 @@ export function AgentSessionsBrowser({
prevViewingSessionRef.current = viewingSession;
}, [viewingSession]);
// Reset aggregate stats when cwd changes (session loading is handled by useSessionPagination)
// Reset aggregate stats when cwd or agentId changes (session loading is handled by useSessionPagination)
useEffect(() => {
setAggregateStats({ totalSessions: 0, totalMessages: 0, totalCostUsd: 0, totalSizeBytes: 0, totalTokens: 0, oldestTimestamp: null, isComplete: false });
}, [activeSession?.cwd]);
}, [activeSession?.cwd, agentId]);
// Listen for progressive stats updates (Claude-specific)
useEffect(() => {
@@ -758,20 +758,14 @@ export function AgentSessionsBrowser({
Total Tokens
</span>
</div>
{(() => {
const totalTokens = viewingSession.inputTokens + viewingSession.outputTokens;
const contextUsage = Math.min(100, (totalTokens / 200000) * 100);
return (
<>
<span className="text-lg font-mono font-semibold" style={{ color: theme.colors.textMain }}>
{formatNumber(totalTokens)}
</span>
<span className="text-[10px] mt-0.5" style={{ color: theme.colors.textDim }}>
of 200k context <span className="font-mono font-medium" style={{ color: theme.colors.accent }}>{contextUsage.toFixed(1)}%</span>
</span>
</>
);
})()}
<div className="flex items-baseline gap-2">
<span className="text-lg font-mono font-semibold" style={{ color: theme.colors.textMain }}>
{formatNumber(viewingSession.inputTokens + viewingSession.outputTokens)}
</span>
<span className="text-[10px]" style={{ color: theme.colors.textDim }}>
of 200k context <span className="font-mono font-medium" style={{ color: theme.colors.accent }}>{Math.min(100, ((viewingSession.inputTokens + viewingSession.outputTokens) / 200000) * 100).toFixed(1)}%</span>
</span>
</div>
</div>
{/* Messages */}

View File

@@ -44,15 +44,39 @@ interface TabProps {
/**
* Get the display name for a tab.
* Priority: name > first UUID octet > "New"
* Priority: name > truncated session ID > "New"
*
* Handles different agent session ID formats:
* - Claude UUID: "abc123-def456-ghi789" → "ABC123" (first octet)
* - OpenCode: "SES_4BCDFE8C5FFE4KC1UV9NSMYEDB" → "SES_4BCD" (prefix + 4 chars)
* - Codex: "thread_abc123..." → "THR_ABC1" (prefix + 4 chars)
*/
function getTabDisplayName(tab: AITab): string {
if (tab.name) {
return tab.name;
}
if (tab.agentSessionId) {
// Return first octet of UUID in uppercase
return tab.agentSessionId.split('-')[0].toUpperCase();
const id = tab.agentSessionId;
// OpenCode format: ses_XXXX... or SES_XXXX...
if (id.toLowerCase().startsWith('ses_')) {
// Return "SES_" + first 4 chars of the ID portion
return `SES_${id.slice(4, 8).toUpperCase()}`;
}
// Codex format: thread_XXXX...
if (id.toLowerCase().startsWith('thread_')) {
// Return "THR_" + first 4 chars of the ID portion
return `THR_${id.slice(7, 11).toUpperCase()}`;
}
// Claude UUID format: has dashes, return first octet
if (id.includes('-')) {
return id.split('-')[0].toUpperCase();
}
// Generic fallback: first 8 chars uppercase
return id.slice(0, 8).toUpperCase();
}
return 'New Session';
}