feat(sessions): add API endpoints for hiding and unhiding sessions
- Implemented new POST endpoints for hiding and unhiding individual and bulk sessions in the configuration. - Added error handling and validation for project and session IDs in the new API routes. - Enhanced the existing session management functionality in the ConfigManager to support these operations.
This commit is contained in:
parent
da1a8998fc
commit
fb66b14d62
3 changed files with 118 additions and 22 deletions
|
|
@ -382,6 +382,94 @@ export function registerConfigRoutes(app: FastifyInstance): void {
|
|||
}
|
||||
);
|
||||
|
||||
// Hide session
|
||||
app.post<{ Body: { projectId: string; sessionId: string } }>(
|
||||
'/api/config/hide-session',
|
||||
async (request) => {
|
||||
try {
|
||||
const { projectId, sessionId } = request.body;
|
||||
if (!projectId || typeof projectId !== 'string') {
|
||||
return { success: false, error: 'Project ID is required and must be a string' };
|
||||
}
|
||||
if (!sessionId || typeof sessionId !== 'string') {
|
||||
return { success: false, error: 'Session ID is required and must be a string' };
|
||||
}
|
||||
|
||||
configManager.hideSession(projectId, sessionId);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error('Error in POST /api/config/hide-session:', error);
|
||||
return { success: false, error: getErrorMessage(error) };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Unhide session
|
||||
app.post<{ Body: { projectId: string; sessionId: string } }>(
|
||||
'/api/config/unhide-session',
|
||||
async (request) => {
|
||||
try {
|
||||
const { projectId, sessionId } = request.body;
|
||||
if (!projectId || typeof projectId !== 'string') {
|
||||
return { success: false, error: 'Project ID is required and must be a string' };
|
||||
}
|
||||
if (!sessionId || typeof sessionId !== 'string') {
|
||||
return { success: false, error: 'Session ID is required and must be a string' };
|
||||
}
|
||||
|
||||
configManager.unhideSession(projectId, sessionId);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error('Error in POST /api/config/unhide-session:', error);
|
||||
return { success: false, error: getErrorMessage(error) };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Bulk hide sessions
|
||||
app.post<{ Body: { projectId: string; sessionIds: string[] } }>(
|
||||
'/api/config/hide-sessions',
|
||||
async (request) => {
|
||||
try {
|
||||
const { projectId, sessionIds } = request.body;
|
||||
if (!projectId || typeof projectId !== 'string') {
|
||||
return { success: false, error: 'Project ID is required and must be a string' };
|
||||
}
|
||||
if (!Array.isArray(sessionIds) || sessionIds.some((id) => typeof id !== 'string')) {
|
||||
return { success: false, error: 'Session IDs must be an array of strings' };
|
||||
}
|
||||
|
||||
configManager.hideSessions(projectId, sessionIds);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error('Error in POST /api/config/hide-sessions:', error);
|
||||
return { success: false, error: getErrorMessage(error) };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Bulk unhide sessions
|
||||
app.post<{ Body: { projectId: string; sessionIds: string[] } }>(
|
||||
'/api/config/unhide-sessions',
|
||||
async (request) => {
|
||||
try {
|
||||
const { projectId, sessionIds } = request.body;
|
||||
if (!projectId || typeof projectId !== 'string') {
|
||||
return { success: false, error: 'Project ID is required and must be a string' };
|
||||
}
|
||||
if (!Array.isArray(sessionIds) || sessionIds.some((id) => typeof id !== 'string')) {
|
||||
return { success: false, error: 'Session IDs must be an array of strings' };
|
||||
}
|
||||
|
||||
configManager.unhideSessions(projectId, sessionIds);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error('Error in POST /api/config/unhide-sessions:', error);
|
||||
return { success: false, error: getErrorMessage(error) };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Select folders - no-op in browser mode
|
||||
app.post('/api/config/select-folders', async (): Promise<ConfigResult<string[]>> => {
|
||||
return { success: true, data: [] };
|
||||
|
|
|
|||
|
|
@ -562,14 +562,18 @@ export async function analyzeSessionFileMetadata(
|
|||
}
|
||||
|
||||
// Last phase: final tokens - last post-compaction
|
||||
// Guard: if the last compaction had no subsequent assistant message, post is 0.
|
||||
// In that case, skip the final phase to avoid double-counting.
|
||||
const lastPhase = compactionPhases[compactionPhases.length - 1];
|
||||
const lastContribution = lastMainAssistantInputTokens - lastPhase.post;
|
||||
total += lastContribution;
|
||||
phaseBreakdown.push({
|
||||
phaseNumber: compactionPhases.length + 1,
|
||||
contribution: lastContribution,
|
||||
peakTokens: lastMainAssistantInputTokens,
|
||||
});
|
||||
if (lastPhase.post > 0) {
|
||||
const lastContribution = lastMainAssistantInputTokens - lastPhase.post;
|
||||
total += lastContribution;
|
||||
phaseBreakdown.push({
|
||||
phaseNumber: compactionPhases.length + 1,
|
||||
contribution: lastContribution,
|
||||
peakTokens: lastMainAssistantInputTokens,
|
||||
});
|
||||
}
|
||||
|
||||
contextConsumption = total;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,23 +413,27 @@ export const ChatHistory = ({ tabId }: ChatHistoryProps): JSX.Element => {
|
|||
const prevItem = aiItemIndex > 0 ? conversation.items[aiItemIndex - 1] : null;
|
||||
if (prevItem?.type !== 'user') return;
|
||||
|
||||
const groupId = prevItem.group.id;
|
||||
const element = chatItemRefs.current.get(groupId);
|
||||
if (!element) return;
|
||||
const run = async (): Promise<void> => {
|
||||
const groupId = prevItem.group.id;
|
||||
await ensureGroupVisible(groupId);
|
||||
const element = chatItemRefs.current.get(groupId);
|
||||
if (!element) return;
|
||||
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
setHighlightedGroupId(groupId);
|
||||
setIsNavigationHighlight(true);
|
||||
if (navigationHighlightTimerRef.current) {
|
||||
clearTimeout(navigationHighlightTimerRef.current);
|
||||
}
|
||||
navigationHighlightTimerRef.current = setTimeout(() => {
|
||||
setHighlightedGroupId(null);
|
||||
setIsNavigationHighlight(false);
|
||||
navigationHighlightTimerRef.current = null;
|
||||
}, 2000);
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
setHighlightedGroupId(groupId);
|
||||
setIsNavigationHighlight(true);
|
||||
if (navigationHighlightTimerRef.current) {
|
||||
clearTimeout(navigationHighlightTimerRef.current);
|
||||
}
|
||||
navigationHighlightTimerRef.current = setTimeout(() => {
|
||||
setHighlightedGroupId(null);
|
||||
setIsNavigationHighlight(false);
|
||||
navigationHighlightTimerRef.current = null;
|
||||
}, 2000);
|
||||
};
|
||||
void run();
|
||||
},
|
||||
[conversation, setHighlightedGroupId]
|
||||
[conversation, ensureGroupVisible, setHighlightedGroupId]
|
||||
);
|
||||
|
||||
// Handler to navigate to a specific tool within a turn from context panel
|
||||
|
|
|
|||
Loading…
Reference in a new issue