function isAssistantMessage(message) {
    return message.role === "assistant";
}
function getStopReason(message) {
    if (!isAssistantMessage(message)) {
        return undefined;
    }
    return typeof message.stopReason === "string" ? message.stopReason : undefined;
}
function addAssistantUsage(details, message) {
    details.api = message.api;
    details.provider = message.provider;
    details.model = message.model;
    details.stopReason = message.stopReason;
    details.inputTokens = message.usage.input;
    details.outputTokens = message.usage.output;
    details.totalTokens = message.usage.totalTokens;
}
export class HubAgentSessionLogger {
    agentId;
    logs;
    now;
    runStartedAt;
    runId;
    runSequence = 0;
    nextTurnIndex = 0;
    activeTurn;
    assistantMessageStartedAt;
    assistantFirstDeltaLogged = false;
    assistantMessageCount = 0;
    toolStartedAt = new Map();
    compactionStartedAt;
    retryStartedAt;
    retryDelayMs;
    constructor(options) {
        this.agentId = options.agentId;
        this.logs = options.logs;
        this.now = options.now ?? (() => Date.now());
    }
    handle(event) {
        if (!this.logs) {
            return;
        }
        switch (event.type) {
            case "agent_start":
                this.handleAgentStart();
                return;
            case "agent_end":
                this.handleAgentEnd();
                return;
            case "turn_start":
                this.handleTurnStart();
                return;
            case "turn_end":
                this.handleTurnEnd(event);
                return;
            case "message_start":
                this.handleMessageStart(event.message);
                return;
            case "message_update":
                this.handleMessageUpdate(event.message);
                return;
            case "message_end":
                this.handleMessageEnd(event.message);
                return;
            case "tool_execution_start":
                this.toolStartedAt.set(event.toolCallId, {
                    startedAt: this.now(),
                    argsBytes: jsonByteLength(event.args),
                });
                return;
            case "tool_execution_end":
                this.handleToolExecutionEnd(event);
                return;
            case "compaction_start":
                this.compactionStartedAt = this.now();
                return;
            case "compaction_end":
                this.handleCompactionEnd(event);
                return;
            case "auto_retry_start":
                this.retryStartedAt = this.now();
                this.retryDelayMs = event.delayMs;
                return;
            case "auto_retry_end":
                this.handleRetryEnd(event);
                return;
            default:
                return;
        }
    }
    handleAgentStart() {
        const now = this.now();
        this.runStartedAt = now;
        this.runSequence += 1;
        this.runId = `${this.agentId}-${now}-${this.runSequence}`;
        this.nextTurnIndex = 0;
        this.activeTurn = undefined;
        this.assistantMessageStartedAt = undefined;
        this.assistantFirstDeltaLogged = false;
        this.assistantMessageCount = 0;
        this.toolStartedAt.clear();
        this.compactionStartedAt = undefined;
        this.retryStartedAt = undefined;
        this.retryDelayMs = undefined;
        this.info("conversation started", { phase: "conversation" });
    }
    handleAgentEnd() {
        const durationMs = this.durationSince(this.runStartedAt);
        this.info("conversation timing summary", {
            phase: "conversation",
            durationMs,
            turns: this.nextTurnIndex,
            messages: this.assistantMessageCount,
        });
        this.runStartedAt = undefined;
        this.activeTurn = undefined;
        this.assistantMessageStartedAt = undefined;
        this.assistantFirstDeltaLogged = false;
        this.toolStartedAt.clear();
        this.compactionStartedAt = undefined;
        this.retryStartedAt = undefined;
        this.retryDelayMs = undefined;
    }
    handleTurnStart() {
        this.activeTurn = {
            startedAt: this.now(),
            turnIndex: this.nextTurnIndex,
        };
        this.nextTurnIndex += 1;
    }
    handleTurnEnd(event) {
        const turn = this.activeTurn;
        this.info("turn timing", {
            phase: "turn",
            durationMs: this.durationSince(turn?.startedAt),
            turnIndex: turn?.turnIndex ?? Math.max(0, this.nextTurnIndex - 1),
            toolResults: event.toolResults.length,
            stopReason: getStopReason(event.message) ?? null,
        });
        this.activeTurn = undefined;
    }
    handleMessageStart(message) {
        if (isAssistantMessage(message)) {
            this.assistantMessageStartedAt = this.now();
            this.assistantFirstDeltaLogged = false;
        }
    }
    handleMessageUpdate(message) {
        if (!isAssistantMessage(message) || this.assistantFirstDeltaLogged) {
            return;
        }
        this.assistantFirstDeltaLogged = true;
        this.info("assistant first delta timing", {
            phase: "assistant_first_delta",
            durationMs: this.durationSince(this.assistantMessageStartedAt),
        });
    }
    handleMessageEnd(message) {
        if (!isAssistantMessage(message)) {
            return;
        }
        this.assistantMessageCount += 1;
        const details = {
            phase: "assistant_message",
            durationMs: this.durationSince(this.assistantMessageStartedAt),
        };
        addAssistantUsage(details, message);
        this.info("assistant message timing", details);
        this.assistantMessageStartedAt = undefined;
        this.assistantFirstDeltaLogged = false;
    }
    handleToolExecutionEnd(event) {
        const tool = this.toolStartedAt.get(event.toolCallId);
        this.toolStartedAt.delete(event.toolCallId);
        this.info("tool timing", {
            phase: "tool",
            durationMs: this.durationSince(tool?.startedAt),
            toolName: event.toolName,
            toolCallId: event.toolCallId,
            isError: event.isError,
            toolArgsBytes: tool?.argsBytes ?? 0,
            toolResultBytes: jsonByteLength(event.result),
        });
    }
    handleCompactionEnd(event) {
        const details = {
            phase: "compaction",
            durationMs: this.durationSince(this.compactionStartedAt),
            reason: event.reason,
            aborted: event.aborted,
            willRetry: event.willRetry,
        };
        if (event.errorMessage) {
            details.error = event.errorMessage;
        }
        this.info("compaction timing", details);
        this.compactionStartedAt = undefined;
    }
    handleRetryEnd(event) {
        const details = {
            phase: "retry",
            durationMs: this.durationSince(this.retryStartedAt),
            attempt: event.attempt,
            success: event.success,
            retryDelayMs: this.retryDelayMs ?? 0,
        };
        if (event.finalError) {
            details.error = event.finalError;
        }
        this.info("retry timing", details);
        this.retryStartedAt = undefined;
        this.retryDelayMs = undefined;
    }
    durationSince(startedAt) {
        if (startedAt === undefined) {
            return 0;
        }
        return Math.max(0, this.now() - startedAt);
    }
    info(message, details) {
        try {
            this.logs?.info(message, {
                agentId: this.agentId,
                runId: this.runId ?? null,
                ...details,
            });
        }
        catch {
            // Observability must never affect agent execution.
        }
    }
}
function jsonByteLength(value) {
    try {
        return Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
    }
    catch {
        return 0;
    }
}
//# sourceMappingURL=hub-agent-session-logger.js.map