// Force strict mode and setup for ESM
"use strict";
import {
  writeStderrLine
} from "./chunk-YUZI3WAC.js";
import {
  stripTerminalControlSequences
} from "./chunk-KN5EBZLT.js";
import "./chunk-T4TZQJ7W.js";
import "./chunk-H6XPXXMH.js";
import "./chunk-5M6IDOMF.js";
import "./chunk-A2HWMJVO.js";
import "./chunk-RCEEXIOO.js";
import "./chunk-KKRTYONM.js";
import "./chunk-TV6K2FB4.js";
import "./chunk-E2UVIFI2.js";
import "./chunk-XU457J4V.js";
import "./chunk-VDDGDZMA.js";
import "./chunk-SPJQO2CE.js";
import "./chunk-SZRN75WQ.js";
import "./chunk-HTO4JFDZ.js";
import "./chunk-CPBF7KYF.js";
import "./chunk-F333XEAU.js";
import "./chunk-6AM5D4ME.js";
import "./chunk-2Z5UX4KI.js";
import "./chunk-ARKANCNX.js";
import "./chunk-L5U3ELZX.js";
import "./chunk-TYLOFD2U.js";
import "./chunk-D5K5YBSN.js";
import "./chunk-4WKAA4KS.js";
import "./chunk-CR3C7WXL.js";
import "./chunk-RTV5TUYB.js";
import "./chunk-VGC4I5JJ.js";
import "./chunk-FBU7WRZI.js";
import "./chunk-Y66VOBRP.js";
import "./chunk-WTNZ6DXB.js";
import "./chunk-JGUT3LWZ.js";
import "./chunk-OKAIGAYW.js";
import "./chunk-ZPAHLFZU.js";
import "./chunk-JESCGQM3.js";
import "./chunk-WIEO4CWB.js";
import "./chunk-XVNQMZ2I.js";
import "./chunk-FEOZBUMT.js";
import "./chunk-VRR65QYW.js";
import "./chunk-A4UCTEB5.js";
import "./chunk-XWRJCPHC.js";
import "./chunk-4LD5APFL.js";
import "./chunk-TYAMGABM.js";
import "./chunk-FCMNKVXT.js";
import "./chunk-DJ2GSRLV.js";
import "./chunk-LQIDP6K5.js";
import {
  createDebugLogger
} from "./chunk-7NFB7LBQ.js";
import "./chunk-75DOP5OR.js";
import "./chunk-DMTGGOSA.js";
import "./chunk-YQ3U5MUC.js";
import "./chunk-TB6UDU4T.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
 */
