orca/src/main/ai-vault/session-title-resolver.ts

47 lines
1.7 KiB
TypeScript

import { extname } from 'node:path'
import { hasUnsafeProviderSessionIdChars } from '../../shared/agent-session-resume'
import {
AI_VAULT_SESSION_TITLE_REQUEST_MAX_COUNT,
type AiVaultSessionTitleRequest,
type AiVaultSessionTitlesResult
} from '../../shared/ai-vault-session-title'
import { resolveAiVaultSessionTitlesInWorker } from './session-scanner-worker-spawn'
const TRANSCRIPT_PATH_MAX_LENGTH = 32_768
function normalizeRequest(request: AiVaultSessionTitleRequest): AiVaultSessionTitleRequest | null {
const sessionId = request.sessionId.trim()
if (!sessionId || sessionId.length > 512 || hasUnsafeProviderSessionIdChars(sessionId)) {
return null
}
const transcriptPath = request.transcriptPath?.trim()
if (
!transcriptPath ||
transcriptPath.length > TRANSCRIPT_PATH_MAX_LENGTH ||
hasUnsafeProviderSessionIdChars(transcriptPath) ||
extname(transcriptPath).toLowerCase() !== '.jsonl'
) {
return { agent: request.agent, sessionId }
}
return { agent: request.agent, sessionId, transcriptPath }
}
export async function resolveLocalAiVaultSessionTitles(
requests: AiVaultSessionTitleRequest[],
signal?: AbortSignal
): Promise<AiVaultSessionTitlesResult> {
const deduped = new Map<string, AiVaultSessionTitleRequest>()
for (const request of requests.slice(0, AI_VAULT_SESSION_TITLE_REQUEST_MAX_COUNT)) {
const normalized = normalizeRequest(request)
if (!normalized) {
continue
}
const key = `${normalized.agent}\0${normalized.sessionId}`
const previous = deduped.get(key)
if (!previous?.transcriptPath || normalized.transcriptPath) {
deduped.set(key, normalized)
}
}
return resolveAiVaultSessionTitlesInWorker([...deduped.values()], signal)
}