// Force strict mode and setup for ESM
"use strict";
import {
  init_esbuild_shims
} from "./chunk-5O2XNYP6.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/cli/src/serve/daemon-metrics-ring.ts
init_esbuild_shims();
var MAX_SAMPLES_PER_WINDOW = 4096;
var DaemonMetricsRing = class {
  static {
    __name(this, "DaemonMetricsRing");
  }
  capacity;
  buckets = [];
  curRequests = 0;
  curErrors = 0;
  curDurations = [];
  curPromptsCompleted = 0;
  curQueueWaits = [];
  curPromptDurations = [];
  curLlmDurations = [];
  curLlmApiErrors = 0;
  curLlmApiRetries = 0;
  curPipeInBytes = 0;
  curPipeOutBytes = 0;
  curTokensIn = 0;
  curTokensOut = 0;
  constructor(options) {
    this.capacity = Math.max(1, Math.floor(options.capacity));
  }
  /** Fold one completed HTTP request into the open window. */
  recordRequest(durationMs, statusCode) {
    this.curRequests += 1;
    if (statusCode >= 400) this.curErrors += 1;
    pushCapped(this.curDurations, durationMs);
  }
  /** Fold one prompt's queue wait (dispatched from the per-session FIFO). */
  recordPromptQueueWait(durationMs) {
    pushCapped(this.curQueueWaits, durationMs);
  }
  /** Fold one finished prompt's end-to-end duration (also counts throughput). */
  recordPromptDuration(durationMs) {
    this.curPromptsCompleted += 1;
    pushCapped(this.curPromptDurations, durationMs);
  }
  /** Fold one model round's LLM API round-trip time (from the token frame). */
  recordLlmDuration(durationMs) {
    pushCapped(this.curLlmDurations, durationMs);
  }
  /**
   * Fold one model round's per-round model-API-error and automatic-retry
   * increments (from the same token frame) into the open window. Both are
   * plain counters — a burst in one window accrues exactly, past the sample
   * cap that only bounds the percentile arrays. Non-finite / negative inputs
   * are ignored so a malformed frame cannot poison the totals.
   */
  recordApiActivity(errors, retries) {
    if (Number.isFinite(errors) && errors > 0) this.curLlmApiErrors += errors;
    if (Number.isFinite(retries) && retries > 0) {
      this.curLlmApiRetries += retries;
    }
  }
  /** Fold one ACP-child pipe message's payload size into the open window. */
  recordPipe(direction, bytes) {
    if (!Number.isFinite(bytes) || bytes < 0) return;
    if (direction === "inbound") this.curPipeInBytes += bytes;
    else this.curPipeOutBytes += bytes;
  }
  /** Fold one model turn's token usage into the open window. */
  recordTokens(inputTokens, outputTokens) {
    if (Number.isFinite(inputTokens) && inputTokens > 0) {
      this.curTokensIn += inputTokens;
    }
    if (Number.isFinite(outputTokens) && outputTokens > 0) {
      this.curTokensOut += outputTokens;
    }
  }
  /**
   * Seal the open window into a bucket stamped at `now` (folding in the
   * gauges), append it, evict the oldest past capacity, then reset the
   * accumulators for the next window.
   */
  sample(now, gauges) {
    this.buckets.push({
      t: now,
      activeSessions: finiteGauge(gauges.activeSessions),
      activePrompts: finiteGauge(gauges.activePrompts),
      queuedPrompts: finiteGauge(gauges.queuedPrompts),
      requests: this.curRequests,
      errors: this.curErrors,
      latencyP50Ms: percentile(this.curDurations, 0.5),
      latencyP95Ms: percentile(this.curDurations, 0.95),
      promptsCompleted: this.curPromptsCompleted,
      promptQueueWaitP95Ms: percentile(this.curQueueWaits, 0.95),
      promptDurationP95Ms: percentile(this.curPromptDurations, 0.95),
      llmApiP50Ms: percentile(this.curLlmDurations, 0.5),
      llmApiP95Ms: percentile(this.curLlmDurations, 0.95),
      llmApiErrors: this.curLlmApiErrors,
      llmApiRetries: this.curLlmApiRetries,
      cpuPercent: finiteGauge(gauges.cpuPercent),
      rssBytes: finiteGauge(gauges.rssBytes),
      heapUsedBytes: finiteGauge(gauges.heapUsedBytes),
      eventLoopLagP99Ms: finiteGauge(gauges.eventLoopLagP99Ms),
      pipeInBytes: this.curPipeInBytes,
      pipeOutBytes: this.curPipeOutBytes,
      sseConnections: finiteGauge(gauges.sseConnections),
      wsConnections: finiteGauge(gauges.wsConnections),
      acpConnections: finiteGauge(gauges.acpConnections),
      rateLimitRejected: finiteGauge(gauges.rateLimitRejected),
      tokensIn: this.curTokensIn,
      tokensOut: this.curTokensOut,
      childCpuPercent: finiteGauge(gauges.childCpuPercent),
      childRssBytes: finiteGauge(gauges.childRssBytes)
    });
    if (this.buckets.length > this.capacity) {
      this.buckets.splice(0, this.buckets.length - this.capacity);
    }
    this.curRequests = 0;
    this.curErrors = 0;
    this.curDurations.length = 0;
    this.curPromptsCompleted = 0;
    this.curQueueWaits.length = 0;
    this.curPromptDurations.length = 0;
    this.curLlmDurations.length = 0;
    this.curLlmApiErrors = 0;
    this.curLlmApiRetries = 0;
    this.curPipeInBytes = 0;
    this.curPipeOutBytes = 0;
    this.curTokensIn = 0;
    this.curTokensOut = 0;
  }
  /** Oldest→newest copy of the retained buckets (safe to serialize). */
  snapshot() {
    return this.buckets.slice();
  }
};
function computeCpuPercent(prev, cur, elapsedMs, coreCount) {
  if (!prev || !cur || elapsedMs <= 0) return 0;
  const cpuUs = cur.user - prev.user + (cur.system - prev.system);
  return Math.min(
    100,
    Math.max(0, cpuUs / (elapsedMs * 1e3) * 100 / coreCount)
  );
}
__name(computeCpuPercent, "computeCpuPercent");
function finiteGauge(value) {
  return Number.isFinite(value) ? value : 0;
}
__name(finiteGauge, "finiteGauge");
function pushCapped(target, value) {
  if (Number.isFinite(value) && value >= 0 && target.length < MAX_SAMPLES_PER_WINDOW) {
    target.push(value);
  }
}
__name(pushCapped, "pushCapped");
function percentile(samples, q) {
  if (samples.length === 0) return 0;
  const sorted = samples.slice().sort((a, b) => a - b);
  const rank = Math.ceil(q * sorted.length);
  const idx = Math.min(sorted.length - 1, Math.max(0, rank - 1));
  return sorted[idx];
}
__name(percentile, "percentile");

export {
  DaemonMetricsRing,
  computeCpuPercent
};
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
