/**
 * Session trace uploader.
 *
 * Listens for agent_end events and uploads the session JSONL (and any
 * subagent JONLs) to the Shortcut backend for centralized trace storage.
 *
 * Subagent linking: when the parent session contains task tool results
 * with sessionFile paths, those subagent JONLs are uploaded too. The
 * parent's JSONL references subagent session IDs via details.sessionId,
 * forming a tree queryable in ClickHouse.
 */
import { existsSync, readFileSync, unlinkSync } from 'fs';
import { SHORTCUT_API_URL } from '../../constants.js';
import { SHORTCUT_API_ENDPOINTS } from '../../endpoints.js';
import { log } from '../../shared/logging/agent-log.js';
/**
 * Upload JSONL content to the trace endpoint.
 * Pure HTTP — no file I/O, no constants. Exported for testing.
 */
export async function uploadTrace(content, baseUrl, accessToken) {
    const response = await fetch(`${baseUrl}${SHORTCUT_API_ENDPOINTS.agentTraces}`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-ndjson',
            Authorization: `Bearer ${accessToken}`
        },
        body: content
    });
    const body = await response.text().catch(() => '');
    return { ok: response.ok, status: response.status, body };
}
/** Subscribe to a session and upload its trace JSONL on agent_end. */
export function attachSessionUpload(session, options) {
    const listener = (event) => {
        if (event.type === 'agent_end') {
            if (!options.isEnabled())
                return;
            options.logger?.info({
                module: 'trace_upload',
                event: 'trace_upload_started'
            });
            void uploadAll(session, options)
                .then((result) => {
                if (result.uploadedFiles === 0 && result.failedFiles === 0) {
                    return;
                }
                if (result.failedFiles > 0) {
                    options.logger?.warn({
                        module: 'trace_upload',
                        event: 'trace_upload_failed',
                        data: {
                            uploaded_files: result.uploadedFiles,
                            failed_files: result.failedFiles
                        }
                    });
                    return;
                }
                options.logger?.info({
                    module: 'trace_upload',
                    event: 'trace_upload_succeeded',
                    data: {
                        uploaded_files: result.uploadedFiles,
                        failed_files: result.failedFiles
                    }
                });
            })
                .catch((e) => {
                options.logger?.error({
                    module: 'trace_upload',
                    event: 'trace_upload_failed',
                    data: {
                        error_message: e instanceof Error ? e.message : String(e)
                    }
                });
                log.error('trace-upload', 'uploadAll failed', e);
            });
        }
    };
    return session.subscribe(listener);
}
/** Upload the parent session JSONL plus any subagent session files referenced in tool results. */
async function uploadAll(session, options) {
    const accessToken = options.getAccessToken();
    if (!accessToken)
        return { uploadedFiles: 0, failedFiles: 0 };
    const sessionFile = session.sessionManager.getSessionFile();
    if (!sessionFile)
        return { uploadedFiles: 0, failedFiles: 0 };
    let uploadedFiles = 0;
    let failedFiles = 0;
    // Upload the parent session
    if (await uploadFileFromDisk(sessionFile, accessToken))
        uploadedFiles++;
    else
        failedFiles++;
    // Find and upload subagent session files from tool result details
    const entries = session.sessionManager.getEntries();
    for (const entry of entries) {
        if (entry.type !== 'message')
            continue;
        const msg = entry.message;
        if (msg?.role !== 'toolResult')
            continue;
        const subFile = msg?.details?.sessionFile;
        if (typeof subFile === 'string' && existsSync(subFile)) {
            if (await uploadFileFromDisk(subFile, accessToken))
                uploadedFiles++;
            else
                failedFiles++;
            if (options.keepSubagentFiles === false) {
                try {
                    unlinkSync(subFile);
                }
                catch {
                    /* ignore */
                }
            }
        }
    }
    return { uploadedFiles, failedFiles };
}
/** Read a JSONL file from disk and upload it; returns false on read errors or empty content. */
async function uploadFileFromDisk(filePath, accessToken) {
    let content;
    try {
        content = readFileSync(filePath, 'utf-8');
    }
    catch {
        return false;
    }
    if (!content.trim())
        return false;
    const result = await uploadTrace(content, SHORTCUT_API_URL, accessToken);
    return result.ok;
}
//# sourceMappingURL=session-upload.js.map