import { n as isTruthyEnvValue } from "./env-DDC-EStD.js";
import { n as appendRegularFileSync } from "./regular-file-Dwz6p59y.js";
import "./regular-file-C2hsuc07.js";
import { t as isDiagnosticFlagEnabled } from "./diagnostic-flags-Doha8xVC.js";
import { mkdirSync } from "node:fs";
import { dirname } from "node:path";
import { randomUUID } from "node:crypto";
import { AsyncLocalStorage } from "node:async_hooks";
import { performance } from "node:perf_hooks";
//#region src/infra/diagnostics-timeline.ts
const OPENCLAW_DIAGNOSTICS_TIMELINE_SCHEMA_VERSION = "openclaw.diagnostics.v1";
let warnedAboutTimelineWrite = false;
const createdTimelineDirs = /* @__PURE__ */ new Set();
const activeDiagnosticsTimelineSpan = new AsyncLocalStorage();
function resolveDiagnosticsTimelineOptions(options = {}) {
	return {
		env: options.env ?? process.env,
		...options.config ? { config: options.config } : {}
	};
}
/** Returns true when diagnostics flags and a JSONL output path both allow timeline writes. */
function isDiagnosticsTimelineEnabled(options = {}) {
	const { config, env } = resolveDiagnosticsTimelineOptions(options);
	return (isDiagnosticFlagEnabled("timeline", config, env) || isDiagnosticFlagEnabled("diagnostics.timeline", config, env) || isTruthyEnvValue(env.OPENCLAW_DIAGNOSTICS)) && typeof env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH === "string" && env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH.trim().length > 0;
}
function normalizeNumber(value) {
	if (typeof value !== "number" || !Number.isFinite(value)) return;
	return Math.max(0, Math.round(value * 1e3) / 1e3);
}
function normalizeAttributes(attributes) {
	if (!attributes) return;
	const normalized = {};
	for (const [key, value] of Object.entries(attributes)) {
		if (typeof value === "number") {
			if (Number.isFinite(value)) normalized[key] = normalizeNumber(value) ?? 0;
			continue;
		}
		if (typeof value === "string" || typeof value === "boolean" || value === null) normalized[key] = value;
	}
	return Object.keys(normalized).length > 0 ? normalized : void 0;
}
function serializeTimelineEvent(event, env) {
	const normalized = {
		schemaVersion: OPENCLAW_DIAGNOSTICS_TIMELINE_SCHEMA_VERSION,
		type: event.type,
		timestamp: event.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
		name: event.name,
		...env.OPENCLAW_DIAGNOSTICS_RUN_ID ? { runId: env.OPENCLAW_DIAGNOSTICS_RUN_ID } : {},
		...env.OPENCLAW_DIAGNOSTICS_ENV ? { envName: env.OPENCLAW_DIAGNOSTICS_ENV } : {},
		pid: process.pid,
		...event.runId ? { runId: event.runId } : {},
		...event.envName ? { envName: event.envName } : {},
		...typeof event.pid === "number" ? { pid: event.pid } : {},
		...event.phase ? { phase: event.phase } : {},
		...event.spanId ? { spanId: event.spanId } : {},
		...event.parentSpanId ? { parentSpanId: event.parentSpanId } : {},
		...typeof event.durationMs === "number" ? { durationMs: normalizeNumber(event.durationMs) } : {},
		...event.errorName ? { errorName: event.errorName } : {},
		...event.errorMessage ? { errorMessage: event.errorMessage } : {},
		...typeof event.p50Ms === "number" ? { p50Ms: normalizeNumber(event.p50Ms) } : {},
		...typeof event.p95Ms === "number" ? { p95Ms: normalizeNumber(event.p95Ms) } : {},
		...typeof event.p99Ms === "number" ? { p99Ms: normalizeNumber(event.p99Ms) } : {},
		...typeof event.maxMs === "number" ? { maxMs: normalizeNumber(event.maxMs) } : {},
		...event.activeSpanName ? { activeSpanName: event.activeSpanName } : {},
		...event.provider ? { provider: event.provider } : {},
		...event.operation ? { operation: event.operation } : {},
		...typeof event.ok === "boolean" ? { ok: event.ok } : {},
		...typeof event.status === "number" ? { status: normalizeNumber(event.status) } : {},
		...event.command ? { command: event.command } : {},
		...event.exitCode !== void 0 ? { exitCode: event.exitCode } : {},
		...event.signal !== void 0 ? { signal: event.signal } : {},
		...normalizeAttributes(event.attributes) ? { attributes: normalizeAttributes(event.attributes) } : {}
	};
	return `${JSON.stringify(normalized)}\n`;
}
/** Appends one normalized diagnostics timeline event to the configured JSONL file. */
function emitDiagnosticsTimelineEvent(event, options = {}) {
	const { env } = resolveDiagnosticsTimelineOptions(options);
	if (!isDiagnosticsTimelineEnabled(options)) return;
	const path = env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH?.trim();
	if (!path) return;
	const line = serializeTimelineEvent(event, env);
	try {
		const dir = dirname(path);
		if (!createdTimelineDirs.has(dir)) {
			mkdirSync(dir, { recursive: true });
			createdTimelineDirs.add(dir);
		}
		appendRegularFileSync({
			filePath: path,
			content: line
		});
	} catch (error) {
		if (!warnedAboutTimelineWrite) {
			warnedAboutTimelineWrite = true;
			console.warn(`[diagnostics] failed to write timeline event: ${String(error)}`);
		}
	}
}
/** Replays a completed span after its activation config becomes available. */
function emitCompletedDiagnosticsTimelineSpan(name, durationMs, options = {}) {
	if (!isDiagnosticsTimelineEnabled(options)) return;
	const spanId = randomUUID();
	emitDiagnosticsTimelineEvent({
		type: "span.start",
		name,
		phase: options.phase,
		spanId,
		parentSpanId: options.parentSpanId,
		attributes: options.attributes
	}, options);
	emitDiagnosticsTimelineEvent({
		type: "span.end",
		name,
		phase: options.phase,
		spanId,
		parentSpanId: options.parentSpanId,
		durationMs,
		attributes: options.attributes
	}, options);
}
/** Returns the currently active span so callers can preserve parentage across memoized work. */
function getActiveDiagnosticsTimelineSpan() {
	return activeDiagnosticsTimelineSpan.getStore();
}
function startDiagnosticsTimelineSpan(name, options) {
	const env = options.env ?? process.env;
	if (!isDiagnosticsTimelineEnabled({
		config: options.config,
		env
	})) return;
	const activeSpan = getActiveDiagnosticsTimelineSpan();
	const phase = options.phase ?? activeSpan?.phase;
	const parentSpanId = options.parentSpanId ?? activeSpan?.spanId;
	const span = {
		name,
		env,
		...options.config ? { config: options.config } : {},
		spanId: randomUUID(),
		startedAt: performance.now(),
		...phase ? { phase } : {},
		...parentSpanId ? { parentSpanId } : {},
		...options.attributes ? { attributes: options.attributes } : {},
		...options.omitErrorMessage ? { omitErrorMessage: true } : {}
	};
	emitDiagnosticsTimelineEvent({
		type: "span.start",
		name: span.name,
		phase: span.phase,
		spanId: span.spanId,
		parentSpanId: span.parentSpanId,
		attributes: span.attributes
	}, {
		config: span.config,
		env: span.env
	});
	return span;
}
function runInDiagnosticsTimelineSpan(span, run) {
	return activeDiagnosticsTimelineSpan.run({
		name: span.name,
		...span.phase ? { phase: span.phase } : {},
		spanId: span.spanId,
		...span.parentSpanId ? { parentSpanId: span.parentSpanId } : {},
		...span.attributes ? { attributes: span.attributes } : {}
	}, run);
}
function emitFinishedDiagnosticsTimelineSpan(span) {
	emitDiagnosticsTimelineEvent({
		type: "span.end",
		name: span.name,
		phase: span.phase,
		spanId: span.spanId,
		parentSpanId: span.parentSpanId,
		durationMs: performance.now() - span.startedAt,
		attributes: span.attributes
	}, {
		config: span.config,
		env: span.env
	});
}
function emitFailedDiagnosticsTimelineSpan(span, error) {
	emitDiagnosticsTimelineEvent({
		type: "span.error",
		name: span.name,
		phase: span.phase,
		spanId: span.spanId,
		parentSpanId: span.parentSpanId,
		durationMs: performance.now() - span.startedAt,
		attributes: span.attributes,
		errorName: error instanceof Error ? error.name : typeof error,
		...span.omitErrorMessage ? {} : { errorMessage: error instanceof Error ? error.message : String(error) }
	}, {
		config: span.config,
		env: span.env
	});
}
/** Measures async work as a start/end timeline span, emitting an error span before rethrowing. */
async function measureDiagnosticsTimelineSpan(name, run, options = {}) {
	const span = startDiagnosticsTimelineSpan(name, options);
	if (!span) return await run();
	try {
		const result = await runInDiagnosticsTimelineSpan(span, () => run());
		emitFinishedDiagnosticsTimelineSpan(span);
		return result;
	} catch (error) {
		emitFailedDiagnosticsTimelineSpan(span, error);
		throw error;
	}
}
/** Measures sync work as a start/end timeline span, emitting an error span before rethrowing. */
function measureDiagnosticsTimelineSpanSync(name, run, options = {}) {
	const span = startDiagnosticsTimelineSpan(name, options);
	if (!span) return run();
	try {
		const result = runInDiagnosticsTimelineSpan(span, run);
		emitFinishedDiagnosticsTimelineSpan(span);
		return result;
	} catch (error) {
		emitFailedDiagnosticsTimelineSpan(span, error);
		throw error;
	}
}
//#endregion
export { measureDiagnosticsTimelineSpan as a, isDiagnosticsTimelineEnabled as i, emitDiagnosticsTimelineEvent as n, measureDiagnosticsTimelineSpanSync as o, getActiveDiagnosticsTimelineSpan as r, emitCompletedDiagnosticsTimelineSpan as t };
