mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
## 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:
@@ -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') {
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user