feat(agents): improve session ID extraction and JSONL file handling
- Fix session ID extraction to use correct field name "session_id" instead of "sessionId" - Add comprehensive database update logging with error handling - Implement cross-project session file search in get_session_output - Add new load_agent_session_history command for robust JSONL loading - Update UI components to prioritize JSONL file loading over fallback methods - Improve error handling and logging throughout the session management flow - Fix BufReader imports and alias conflicts in Tauri backend This enhances the reliability of agent session tracking and output retrieval by properly handling Claude Code's actual JSON structure and implementing better fallback mechanisms for session data access.
This commit is contained in:
@@ -116,23 +116,81 @@ export function AgentRunOutputViewer({
|
||||
const loadOutput = async (skipCache = false) => {
|
||||
if (!run.id) return;
|
||||
|
||||
console.log('[AgentRunOutputViewer] Loading output for run:', {
|
||||
runId: run.id,
|
||||
status: run.status,
|
||||
sessionId: run.session_id,
|
||||
skipCache
|
||||
});
|
||||
|
||||
try {
|
||||
// Check cache first if not skipping cache
|
||||
if (!skipCache) {
|
||||
const cached = getCachedOutput(run.id);
|
||||
if (cached) {
|
||||
console.log('[AgentRunOutputViewer] Found cached output');
|
||||
const cachedJsonlLines = cached.output.split('\n').filter(line => line.trim());
|
||||
setRawJsonlOutput(cachedJsonlLines);
|
||||
setMessages(cached.messages);
|
||||
// If cache is recent (less than 5 seconds old) and session isn't running, use cache only
|
||||
if (Date.now() - cached.lastUpdated < 5000 && run.status !== 'running') {
|
||||
console.log('[AgentRunOutputViewer] Using recent cache, skipping refresh');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
// If we have a session_id, try to load from JSONL file first
|
||||
if (run.session_id && run.session_id !== '') {
|
||||
console.log('[AgentRunOutputViewer] Attempting to load from JSONL with session_id:', run.session_id);
|
||||
try {
|
||||
const history = await api.loadAgentSessionHistory(run.session_id);
|
||||
console.log('[AgentRunOutputViewer] Successfully loaded JSONL history:', history.length, 'messages');
|
||||
|
||||
// Convert history to messages format
|
||||
const loadedMessages: ClaudeStreamMessage[] = history.map(entry => ({
|
||||
...entry,
|
||||
type: entry.type || "assistant"
|
||||
}));
|
||||
|
||||
setMessages(loadedMessages);
|
||||
setRawJsonlOutput(history.map(h => JSON.stringify(h)));
|
||||
|
||||
// Update cache
|
||||
setCachedOutput(run.id, {
|
||||
output: history.map(h => JSON.stringify(h)).join('\n'),
|
||||
messages: loadedMessages,
|
||||
lastUpdated: Date.now(),
|
||||
status: run.status
|
||||
});
|
||||
|
||||
// Set up live event listeners for running sessions
|
||||
if (run.status === 'running') {
|
||||
console.log('[AgentRunOutputViewer] Setting up live listeners for running session');
|
||||
setupLiveEventListeners();
|
||||
|
||||
try {
|
||||
await api.streamSessionOutput(run.id);
|
||||
} catch (streamError) {
|
||||
console.warn('[AgentRunOutputViewer] Failed to start streaming, will poll instead:', streamError);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (err) {
|
||||
console.warn('[AgentRunOutputViewer] Failed to load from JSONL:', err);
|
||||
console.warn('[AgentRunOutputViewer] Falling back to regular output method');
|
||||
}
|
||||
} else {
|
||||
console.log('[AgentRunOutputViewer] No session_id available, using fallback method');
|
||||
}
|
||||
|
||||
// Fallback to the original method if JSONL loading fails or no session_id
|
||||
console.log('[AgentRunOutputViewer] Using getSessionOutput fallback');
|
||||
const rawOutput = await api.getSessionOutput(run.id);
|
||||
console.log('[AgentRunOutputViewer] Received raw output:', rawOutput.length, 'characters');
|
||||
|
||||
// Parse JSONL output into messages
|
||||
const jsonlLines = rawOutput.split('\n').filter(line => line.trim());
|
||||
@@ -144,9 +202,10 @@ export function AgentRunOutputViewer({
|
||||
const message = JSON.parse(line) as ClaudeStreamMessage;
|
||||
parsedMessages.push(message);
|
||||
} catch (err) {
|
||||
console.error("Failed to parse message:", err, line);
|
||||
console.error("[AgentRunOutputViewer] Failed to parse message:", err, line);
|
||||
}
|
||||
}
|
||||
console.log('[AgentRunOutputViewer] Parsed', parsedMessages.length, 'messages from output');
|
||||
setMessages(parsedMessages);
|
||||
|
||||
// Update cache
|
||||
@@ -159,12 +218,13 @@ export function AgentRunOutputViewer({
|
||||
|
||||
// Set up live event listeners for running sessions
|
||||
if (run.status === 'running') {
|
||||
console.log('[AgentRunOutputViewer] Setting up live listeners for running session (fallback)');
|
||||
setupLiveEventListeners();
|
||||
|
||||
try {
|
||||
await api.streamSessionOutput(run.id);
|
||||
} catch (streamError) {
|
||||
console.warn('Failed to start streaming, will poll instead:', streamError);
|
||||
console.warn('[AgentRunOutputViewer] Failed to start streaming (fallback), will poll instead:', streamError);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
@@ -64,7 +64,25 @@ export const AgentRunView: React.FC<AgentRunViewProps> = ({
|
||||
const runData = await api.getAgentRunWithRealTimeMetrics(runId);
|
||||
setRun(runData);
|
||||
|
||||
// Parse JSONL output into messages
|
||||
// If we have a session_id, try to load from JSONL file first
|
||||
if (runData.session_id && runData.session_id !== '') {
|
||||
try {
|
||||
const history = await api.loadAgentSessionHistory(runData.session_id);
|
||||
|
||||
// Convert history to messages format
|
||||
const loadedMessages: ClaudeStreamMessage[] = history.map(entry => ({
|
||||
...entry,
|
||||
type: entry.type || "assistant"
|
||||
}));
|
||||
|
||||
setMessages(loadedMessages);
|
||||
return;
|
||||
} catch (err) {
|
||||
console.warn('Failed to load from JSONL, falling back to output field:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Parse JSONL output from the output field
|
||||
if (runData.output) {
|
||||
const parsedMessages: ClaudeStreamMessage[] = [];
|
||||
const lines = runData.output.split('\n').filter(line => line.trim());
|
||||
|
@@ -109,6 +109,47 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
// If we have a session_id, try to load from JSONL file first
|
||||
if (session.session_id && session.session_id !== '') {
|
||||
try {
|
||||
const history = await api.loadAgentSessionHistory(session.session_id);
|
||||
|
||||
// Convert history to messages format using AgentExecution style
|
||||
const loadedMessages: ClaudeStreamMessage[] = history.map(entry => ({
|
||||
...entry,
|
||||
type: entry.type || "assistant"
|
||||
}));
|
||||
|
||||
setMessages(loadedMessages);
|
||||
setRawJsonlOutput(history.map(h => JSON.stringify(h)));
|
||||
|
||||
// Update cache
|
||||
setCachedOutput(session.id, {
|
||||
output: history.map(h => JSON.stringify(h)).join('\n'),
|
||||
messages: loadedMessages,
|
||||
lastUpdated: Date.now(),
|
||||
status: session.status
|
||||
});
|
||||
|
||||
// Set up live event listeners for running sessions
|
||||
if (session.status === 'running') {
|
||||
setupLiveEventListeners();
|
||||
|
||||
try {
|
||||
await api.streamSessionOutput(session.id);
|
||||
} catch (streamError) {
|
||||
console.warn('Failed to start streaming, will poll instead:', streamError);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (err) {
|
||||
console.warn('Failed to load from JSONL, falling back to regular output:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to the original method if JSONL loading fails or no session_id
|
||||
const rawOutput = await api.getSessionOutput(session.id);
|
||||
|
||||
// Parse JSONL output into messages using AgentExecution style
|
||||
|
@@ -925,6 +925,21 @@ export const api = {
|
||||
return invoke("load_session_history", { sessionId, projectId });
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads the JSONL history for a specific agent session
|
||||
* Similar to loadSessionHistory but searches across all project directories
|
||||
* @param sessionId - The session ID (UUID)
|
||||
* @returns Promise resolving to array of session messages
|
||||
*/
|
||||
async loadAgentSessionHistory(sessionId: string): Promise<any[]> {
|
||||
try {
|
||||
return await invoke<any[]>('load_agent_session_history', { sessionId });
|
||||
} catch (error) {
|
||||
console.error("Failed to load agent session history:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes a new interactive Claude Code session with streaming output
|
||||
*/
|
||||
|
Reference in New Issue
Block a user