// Force strict mode and setup for ESM
"use strict";
import {
  writeStderrLine
} from "./chunk-OWAK7LMW.js";
import {
  stripTerminalControlSequences
} from "./chunk-QQOPXQYM.js";
import "./chunk-TWPJO254.js";
import "./chunk-H6XPXXMH.js";
import "./chunk-5M6IDOMF.js";
import "./chunk-UPUG4GXM.js";
import "./chunk-64LQLPL7.js";
import "./chunk-2LQTH543.js";
import "./chunk-KRXPRVGL.js";
import "./chunk-GNCIZTNJ.js";
import "./chunk-CVHIL5IG.js";
import "./chunk-IRH27ZC2.js";
import "./chunk-QHWCP53L.js";
import "./chunk-PONHVBRT.js";
import "./chunk-T26EAKDL.js";
import "./chunk-6PVPNMXU.js";
import "./chunk-CPBF7KYF.js";
import "./chunk-6QIGFD5W.js";
import "./chunk-XWWKIXVD.js";
import "./chunk-75GFAX4R.js";
import "./chunk-OKF7JF3R.js";
import "./chunk-JSD33JG6.js";
import "./chunk-S6LOFUVP.js";
import "./chunk-JCW2R7WC.js";
import "./chunk-2DN4J57Y.js";
import "./chunk-ZPJWUGCS.js";
import "./chunk-2LKVPASC.js";
import "./chunk-NAVJD2PQ.js";
import "./chunk-LW3RFMG7.js";
import "./chunk-W4MIWP6M.js";
import "./chunk-DGEIYUC3.js";
import "./chunk-MPHPFVKK.js";
import "./chunk-GLNBDYUX.js";
import "./chunk-DJ2GSRLV.js";
import "./chunk-6D2RWQMA.js";
import "./chunk-75MIQ3LV.js";
import "./chunk-2J3OJGTL.js";
import "./chunk-P3QQPMQA.js";
import "./chunk-RPLGZJV2.js";
import "./chunk-GLCZKT5V.js";
import "./chunk-DZLYR4U3.js";
import "./chunk-NKHOVMWF.js";
import "./chunk-VGC4I5JJ.js";
import "./chunk-SGFT55XI.js";
import "./chunk-Y4IHBGBP.js";
import "./chunk-Y6AXW2OG.js";
import "./chunk-2M3XQKPR.js";
import "./chunk-46UV252V.js";
import "./chunk-ZBVS26BY.js";
import "./chunk-WZRWHXFE.js";
import "./chunk-A3NDJ5SB.js";
import "./chunk-7XATOOE7.js";
import "./chunk-BGTQCTQW.js";
import "./chunk-W4RQUHY3.js";
import "./chunk-S7W2GKIO.js";
import {
  createDebugLogger
} from "./chunk-UHUSJ6X7.js";
import "./chunk-VOQXFAY5.js";
import "./chunk-75DOP5OR.js";
import "./chunk-DMTGGOSA.js";
import "./chunk-YQ3U5MUC.js";
import "./chunk-AMDSOFFV.js";
import "./chunk-QUGAUQGJ.js";
import {
  init_esbuild_shims
} from "./chunk-5O2XNYP6.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/cli/src/serve/create-sub-session.ts
init_esbuild_shims();
import { randomUUID } from "node:crypto";
var log = createDebugLogger("SUB_SESSION");
var MAX_CONCURRENT_SUB_SESSIONS_PER_CALLER = 5;
var MAX_CONCURRENT_SUB_SESSIONS_TOTAL = 20;
var FIRST_TURN_TIMEOUT_MS = 5 * 6e4;
var SENT_MODE_DRAIN_TIMEOUT_MS = 30 * 6e4;
var MAX_RESULT_CHARS = 32e3;
var MAX_NAME_LENGTH = 60;
var MAX_TRACKED_SPAWNED_SESSIONS = 1024;
var BIDI_CONTROL_MARKS = new RegExp(
  "[\\u061C\\u200E\\u200F\\u202A-\\u202E\\u2066-\\u2069]",
  "g"
);
function subSessionName(label) {
  const cleaned = stripTerminalControlSequences(label).replace(BIDI_CONTROL_MARKS, "").trim().replace(/\s+/g, " ");
  let short = cleaned;
  if (cleaned.length > MAX_NAME_LENGTH) {
    let cut = MAX_NAME_LENGTH - 1;
    const boundary = cleaned.charCodeAt(cut - 1);
    if (boundary >= 55296 && boundary <= 56319) cut -= 1;
    short = `${cleaned.slice(0, cut)}\u2026`;
  }
  return `\u{1F9F5} ${short}`;
}
__name(subSessionName, "subSessionName");
async function awaitFirstTurn(bridge, sessionId, promptId, lastEventId, timeoutMs, stopSignal) {
  const ac = new AbortController();
  let timedOut = false;
  const timer = setTimeout(() => {
    timedOut = true;
    ac.abort();
  }, timeoutMs);
  if (typeof timer.unref === "function") timer.unref();
  const composed = stopSignal ? AbortSignal.any([ac.signal, stopSignal]) : ac.signal;
  let acc = "";
  let truncated = false;
  let stopReason;
  const appendChunk = /* @__PURE__ */ __name((text) => {
    if (truncated) return;
    if (acc.length + text.length > MAX_RESULT_CHARS) {
      let cut = Math.max(0, MAX_RESULT_CHARS - acc.length);
      if (cut > 0) {
        const code = text.charCodeAt(cut - 1);
        if (code >= 55296 && code <= 56319) cut -= 1;
      }
      acc += text.slice(0, cut);
      truncated = true;
    } else {
      acc += text;
    }
  }, "appendChunk");
  try {
    for await (const e of bridge.subscribeEvents(sessionId, {
      lastEventId,
      signal: composed
    })) {
      if (e.type === "session_update") {
        const d = e.data;
        if (d?.update?.sessionUpdate === "agent_message_chunk" && typeof d.update.content?.text === "string") {
          appendChunk(d.update.content.text);
        }
      } else if (e.type === "turn_complete") {
        const d = e.data;
        if (d?.promptId === promptId) {
          stopReason = d.stopReason ?? "end_turn";
          break;
        }
      } else if (e.type === "turn_error") {
        const d = e.data;
        if (d?.promptId === promptId) {
          stopReason = "error";
          if (d.message && !truncated) {
            const suffix = `${acc ? "\n" : ""}[turn error] ${d.message}`;
            if (acc.length + suffix.length <= MAX_RESULT_CHARS) {
              acc += suffix;
            } else {
              truncated = true;
            }
          }
          break;
        }
      }
    }
  } finally {
    clearTimeout(timer);
    ac.abort();
  }
  if (stopReason === void 0) {
    stopReason = stopSignal?.aborted ? "shutdown" : timedOut ? "timeout" : "incomplete";
  }
  if (truncated) acc += "\n[\u2026output truncated]";
  return { result: acc, stopReason };
}
__name(awaitFirstTurn, "awaitFirstTurn");
function createSubSessionLauncher(opts) {
  const { getBridge, boundWorkspace } = opts;
  const firstTurnTimeoutMs = opts.firstTurnTimeoutMs ?? FIRST_TURN_TIMEOUT_MS;
  const sentModeDrainTimeoutMs = opts.sentModeDrainTimeoutMs ?? SENT_MODE_DRAIN_TIMEOUT_MS;
  const inflight = /* @__PURE__ */ new Map();
  const spawnedSessionIds = /* @__PURE__ */ new Set();
  const stopAc = new AbortController();
  let inflightTotal = 0;
  const release = /* @__PURE__ */ __name((key) => {
    const n = (inflight.get(key) ?? 1) - 1;
    if (n <= 0) inflight.delete(key);
    else inflight.set(key, n);
    inflightTotal = Math.max(0, inflightTotal - 1);
  }, "release");
  const rememberSpawned = /* @__PURE__ */ __name((sessionId) => {
    spawnedSessionIds.add(sessionId);
    while (spawnedSessionIds.size > MAX_TRACKED_SPAWNED_SESSIONS) {
      const oldest = spawnedSessionIds.values().next().value;
      if (oldest === void 0) break;
      spawnedSessionIds.delete(oldest);
    }
  }, "rememberSpawned");
  const launch = /* @__PURE__ */ __name(async (info) => {
    if (stopAc.signal.aborted) {
      throw new Error(
        "The daemon is shutting down; cannot create a sub-session."
      );
    }
    const bridge = getBridge();
    if (!bridge) {
      throw new Error("Session bridge is not available.");
    }
    if (spawnedSessionIds.has(info.callerSessionId)) {
      throw new Error(
        "A sub-session cannot create further sub-sessions (nesting is capped at one level)."
      );
    }
    const key = info.callerSessionId;
    const current = inflight.get(key) ?? 0;
    if (current >= MAX_CONCURRENT_SUB_SESSIONS_PER_CALLER) {
      throw new Error(
        `Too many concurrent sub-sessions for this session (cap ${MAX_CONCURRENT_SUB_SESSIONS_PER_CALLER}); wait for one to finish.`
      );
    }
    if (inflightTotal >= MAX_CONCURRENT_SUB_SESSIONS_TOTAL) {
      throw new Error(
        `Too many concurrent sub-sessions in this workspace (cap ${MAX_CONCURRENT_SUB_SESSIONS_TOTAL}); wait for one to finish.`
      );
    }
    inflight.set(key, current + 1);
    inflightTotal += 1;
    let released = false;
    const releaseOnce = /* @__PURE__ */ __name(() => {
      if (released) return;
      released = true;
      release(key);
    }, "releaseOnce");
    let spawnedSessionId;
    try {
      const sub = await bridge.spawnOrAttach({
        workspaceCwd: boundWorkspace,
        sessionScope: "thread",
        // force a fresh top-level session, never attach
        // Record the caller as the sub-session's parent so the UI can link it
        // back. Persisted into the sub-session's transcript at spawn time.
        parentSessionId: info.callerSessionId,
        ...info.model ? { modelServiceId: info.model } : {}
      });
      spawnedSessionId = sub.sessionId;
      const sessionId = sub.sessionId;
      rememberSpawned(sessionId);
      try {
        bridge.updateSessionMetadata(sessionId, {
          displayName: subSessionName(info.name ?? info.prompt)
        });
      } catch (err) {
        log.debug("sub-session: updateSessionMetadata failed", sessionId, err);
      }
      const lastEventId = bridge.getSessionLastEventId(sessionId);
      const promptId = randomUUID();
      const turn = bridge.sendPrompt(
        sessionId,
        {
          sessionId,
          prompt: [{ type: "text", text: info.prompt }]
        },
        void 0,
        { promptId }
      );
      void turn.catch((err) => {
        log.debug("sub-session: sendPrompt rejected", sessionId, String(err));
      });
      if (info.completion === "sent") {
        const drainAc = new AbortController();
        let drainTimedOut = false;
        const drainTimer = setTimeout(() => {
          drainTimedOut = true;
          drainAc.abort();
        }, sentModeDrainTimeoutMs);
        if (typeof drainTimer.unref === "function") drainTimer.unref();
        const drainSignal = AbortSignal.any([stopAc.signal, drainAc.signal]);
        void (async () => {
          try {
            const turnSettled = turn.then(
              () => "ok",
              () => "rejected"
            );
            const drainDone = (async () => {
              for await (const e of bridge.subscribeEvents(sessionId, {
                lastEventId,
                signal: drainSignal
              })) {
                if (e.type === "turn_complete" || e.type === "turn_error") {
                  const d = e.data;
                  if (d?.promptId === promptId) break;
                }
              }
              return "drained";
            })();
            await Promise.race([turnSettled, drainDone]);
          } catch (err) {
            if (!(err instanceof Error && err.name === "AbortError") && !(stopAc.signal.aborted || drainAc.signal.aborted)) {
              log.debug(
                "sub-session: sent-mode drain error",
                sessionId,
                String(err)
              );
            }
          } finally {
            clearTimeout(drainTimer);
            if (drainTimedOut) {
              writeStderrLine(
                `qwen serve: sub-session ${sessionId} drain timed out after ${Math.round(sentModeDrainTimeoutMs / 6e4)}min; releasing its concurrency slot (the sub-session may still be running)`
              );
            }
            drainAc.abort();
            releaseOnce();
          }
        })();
        return {
          sessionId,
          ...sub.parentSessionPersisted !== void 0 ? { parentSessionPersisted: sub.parentSessionPersisted } : {}
        };
      }
      try {
        const turnError = turn.then(
          () => new Promise(() => {
          }),
          // never resolves on success
          (err) => Promise.reject(
            new Error(
              `sub-session dispatch failed: ${err instanceof Error ? err.message : String(err)}`
            )
          )
        );
        const firstTurn = awaitFirstTurn(
          bridge,
          sessionId,
          promptId,
          lastEventId,
          firstTurnTimeoutMs,
          stopAc.signal
        );
        const { result, stopReason } = await Promise.race([
          firstTurn,
          turnError
        ]);
        return {
          sessionId,
          result,
          stopReason,
          ...sub.parentSessionPersisted !== void 0 ? { parentSessionPersisted: sub.parentSessionPersisted } : {}
        };
      } finally {
        releaseOnce();
      }
    } catch (err) {
      releaseOnce();
      if (spawnedSessionId !== void 0) {
        try {
          void bridge.closeSession(spawnedSessionId).catch(() => {
          });
        } catch (closeErr) {
          log.debug(
            "sub-session: closeSession threw",
            spawnedSessionId,
            closeErr
          );
        }
      }
      writeStderrLine(
        `qwen serve: create_sub_session failed: ${err instanceof Error ? err.message : String(err)}`
      );
      throw err instanceof Error ? err : new Error(String(err));
    }
  }, "launch");
  return {
    launch,
    stop: /* @__PURE__ */ __name(() => {
      stopAc.abort();
    }, "stop")
  };
}
__name(createSubSessionLauncher, "createSubSessionLauncher");
export {
  MAX_CONCURRENT_SUB_SESSIONS_PER_CALLER,
  MAX_CONCURRENT_SUB_SESSIONS_TOTAL,
  createSubSessionLauncher
};
/**
 * @license
 * Copyright 2025 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
