// Force strict mode and setup for ESM
"use strict";
import {
  MAX_REMEMBER_CONTENT_BYTES,
  extractRememberErrorCode,
  isValidServerName
} from "./chunk-RM3PGSGL.js";
import {
  openQwenAsrRealtimeStream,
  openVoiceStream,
  openVoiceStreamWithRetry
} from "./chunk-PJNLE3B3.js";
import {
  SetupGithubError,
  setupGithub
} from "./chunk-L7QPM2YD.js";
import "./chunk-54YZOP7P.js";
import "./chunk-YBTKKNGO.js";
import {
  CAPABILITIES_SCHEMA_VERSION,
  CLIENT_ID_HEADER,
  CLIENT_ID_RE,
  ClientMcpSenderRegistry,
  DeviceFlowRegistry,
  MAX_CLIENT_ID_LENGTH,
  MAX_SERVER_NAME_LENGTH,
  MAX_TOOL_NAME_LENGTH,
  TooManyActiveDeviceFlowsError,
  UnsupportedDeviceFlowProviderError,
  UpstreamDeviceFlowError,
  allowOriginCors,
  bearerAuth,
  brandSecret,
  buildDaemonStatusResponse,
  createBridgeFileSystemAdapter,
  createBuildWorkspaceCtx,
  createClientMcpServerProvider,
  createMutationGate,
  createRateLimiter,
  denyBrowserOriginCors,
  detectFromLoopback,
  getAdvertisedServeFeatures,
  getServeProtocolVersions,
  hostAllowlist,
  parseAllowOriginPatterns,
  parseAndValidateWorkspaceClientId,
  parseClientIdHeader,
  parseDaemonStatusDetail,
  parseLastEventId,
  parseMaxQueuedQuery,
  parseOptionalWorkspaceCwd,
  parsePermissionVoteBody,
  requireSessionId,
  require_express,
  safeBody,
  safeLogValue,
  sanitizeForStderr,
  sendJsonBodyParserError,
  setDeviceFlowRegistry,
  setRateLimiter,
  unsafeRevealSecret,
  validateMcpRuntimeServerName
} from "./chunk-B7KGXIPZ.js";
import {
  isLoopbackBind
} from "./chunk-RNBYOUGV.js";
import "./chunk-3HX5LZ6R.js";
import "./chunk-7RYW5LQV.js";
import {
  createDaemonStatusProvider
} from "./chunk-NOPIG24K.js";
import "./chunk-J6L3MJQL.js";
import "./chunk-KHDZHZMH.js";
import "./chunk-W6X4FGWX.js";
import {
  createAcpSessionBridge
} from "./chunk-IKZMJLCQ.js";
import "./chunk-EWALXUAD.js";
import {
  EVENT_SCHEMA_VERSION,
  SubscriberLimitExceededError
} from "./chunk-ALX6BUYW.js";
import "./chunk-O3TKZZJO.js";
import {
  createWorkspaceProvidersStatusProvider
} from "./chunk-QNNPLKUL.js";
import {
  getAuthTypeFromEnv,
  resolveCliGenerationConfig
} from "./chunk-OD6TMXL7.js";
import {
  EMPTY_WORKSPACE_VOICE_UPDATE_ERROR,
  WorkspaceVoiceError,
  buildWorkspaceVoiceSettingsWrites,
  buildWorkspaceVoiceStatus,
  createDaemonWorkspaceService,
  hasConfiguredBatchVoiceTranscriptionModel,
  transcribeWorkspaceVoiceAudio,
  validateWorkspaceVoiceState,
  voiceSettingsScopeToWire
} from "./chunk-ZAXSLRXO.js";
import {
  PermissionRulesValidationError,
  buildPermissionSettings,
  isPermissionRuleType,
  normalizePermissionRules,
  readPermissionRuleSet
} from "./chunk-GRE6HMB4.js";
import {
  assertVoiceBaseUrlNetworkAllowed,
  isStreamingVoiceModel,
  resolveVoiceStreamConfig,
  resolveVoiceTranscriptionConfig,
  sanitizeVoiceErrorMessage,
  transcribeVoiceAudio
} from "./chunk-C2Z6TUIE.js";
import {
  isVoiceEnabled,
  isVoiceMode,
  readVoiceModel
} from "./chunk-L6EZL7CD.js";
import "./chunk-NIOPRZNN.js";
import "./chunk-P7M4GA57.js";
import {
  BranchWhilePromptActiveError,
  CancelSentinelCollisionError,
  CdWhilePromptActiveError,
  InvalidClientIdError,
  InvalidPermissionOptionError,
  InvalidRewindTargetError,
  InvalidSessionMetadataError,
  InvalidSessionScopeError,
  McpServerNotFoundError,
  McpServerRestartFailedError,
  PermissionForbiddenError,
  PermissionPolicyNotImplementedError,
  PromptQueueFullError,
  RestoreInProgressError,
  SessionArchivedError,
  SessionArchivingError,
  SessionBusyError,
  SessionConflictError,
  SessionLimitExceededError,
  SessionNotFoundError,
  SessionShellClientRequiredError,
  SessionShellDisabledError,
  WorkspaceInitConflictError,
  WorkspaceInitPathEscapeError,
  WorkspaceInitRaceError,
  WorkspaceInitSymlinkError,
  WorkspaceMismatchError
} from "./chunk-T22H4V4R.js";
import {
  MAX_WORKSPACE_PATH_LENGTH,
  canonicalizeWorkspace
} from "./chunk-SEZC2725.js";
import {
  STATUS_SCHEMA_VERSION,
  createIdleWorkspaceMemoryStatus,
  mapDomainErrorToErrorKind
} from "./chunk-TK7QG7DS.js";
import {
  WorkspacePermissionRulesSessionRequiredError,
  WorkspaceSettingsPartialPersistError
} from "./chunk-BMFMJINR.js";
import {
  getDialogSettingKeys,
  getNestedProperty,
  getSettingDefinition,
  loadSettings,
  validateSettingValue
} from "./chunk-WVMLL7UN.js";
import {
  SUPPORTED_LANGUAGES
} from "./chunk-IYOWA7FD.js";
import "./chunk-TLRYABYP.js";
import "./chunk-UFC57OYT.js";
import "./chunk-PPFWEV7G.js";
import {
  getWorkspaceTrustStatus
} from "./chunk-7HRRYBVZ.js";
import {
  writeStderrLine
} from "./chunk-Y6Z2O3WR.js";
import "./chunk-QYUE6W3T.js";
import {
  ALL_PROVIDERS,
  APPROVAL_MODES,
  BTW_MAX_INPUT_LENGTH,
  BuiltinAgentRegistry,
  Client,
  ClientMcpRegistrar,
  ExtensionManager,
  ModelsConfig,
  RUNTIME_MCP_IF_ABSENT_CONFIG_FLAG,
  SessionService,
  StandardFileSystemService,
  StdioClientTransport,
  StreamableHTTPClientTransport,
  SubagentError,
  SubagentErrorCode,
  SubagentManager,
  TrustGateError,
  WorkspaceMemoryFileTooLargeError,
  WorkspaceMemoryWriteTimeoutError,
  checkForExtensionUpdate,
  detectLineEnding,
  encodeTextFileContent,
  glob,
  loadIgnoreRules,
  parseInstallSource,
  redactUrlCredentials,
  shouldShowStep,
  writeWorkspaceContextFile
} from "./chunk-SS2LXHYC.js";
import "./chunk-5P5XGNYH.js";
import "./chunk-K5PGHDBN.js";
import "./chunk-SKAZWEV5.js";
import "./chunk-QOMAHFYI.js";
import "./chunk-G7OQ3RFJ.js";
import "./chunk-LKWAVAB5.js";
import "./chunk-MEQWZGK5.js";
import "./chunk-UANCYOOQ.js";
import "./chunk-2MPVVENX.js";
import "./chunk-ZTQ26VBE.js";
import "./chunk-SJBIFVD2.js";
import "./chunk-MLZQVCF3.js";
import "./chunk-S5EQPDTM.js";
import "./chunk-LQ7TMOCE.js";
import "./chunk-E5A7LHNN.js";
import "./chunk-GO6LNQXT.js";
import "./chunk-B236XPGF.js";
import "./chunk-YXK4GPYO.js";
import "./chunk-TOAUDIS7.js";
import "./chunk-NPQWQYU6.js";
import {
  addDaemonRequestAttribute,
  decodeBufferWithEncodingInfo,
  emitDaemonLog,
  getAllGeminiMdFilenames,
  hashDaemonWorkspace,
  isBinaryFile,
  isWithinRoot,
  recordDaemonBridgeError,
  recordDaemonError,
  recordDaemonHttpRequest,
  recordDaemonHttpResponse,
  withDaemonRequestSpan
} from "./chunk-DEUDX7RW.js";
import "./chunk-E6US47KI.js";
import "./chunk-UWCTAVOD.js";
import "./chunk-OFEVLU4C.js";
import "./chunk-OMX7CUOE.js";
import "./chunk-MOUXUCXQ.js";
import "./chunk-BZHLWC6T.js";
import "./chunk-UQFP2FEF.js";
import "./chunk-4VZH35PN.js";
import {
  QwenOAuth2Client,
  QwenOAuthPollError,
  cacheQwenCredentials,
  generatePKCEPair,
  isDeviceAuthorizationSuccess,
  isDeviceTokenPending,
  isDeviceTokenSuccess
} from "./chunk-TENOUWY2.js";
import "./chunk-GLPV75ES.js";
import "./chunk-22IFUCVR.js";
import "./chunk-T42ACHFI.js";
import "./chunk-YTFBURQD.js";
import "./chunk-YPCPZSZB.js";
import "./chunk-IRJWHZWU.js";
import "./chunk-SYCJMSIJ.js";
import "./chunk-55ZMG67I.js";
import {
  import_websocket_server
} from "./chunk-H6BD2ELD.js";
import "./chunk-5IFG2VC4.js";
import "./chunk-64WXLC72.js";
import "./chunk-D5KISTLW.js";
import {
  FatalConfigError,
  Storage,
  createDebugLogger
} from "./chunk-AVQDCKNF.js";
import "./chunk-ZERZSAZL.js";
import "./chunk-QN5NZ3UQ.js";
import "./chunk-BR4QREVK.js";
import "./chunk-Z2Z3GUXZ.js";
import {
  init_esbuild_shims
} from "./chunk-A4BMJM77.js";
import {
  __name,
  __toESM
} from "./chunk-J2S4EL5Y.js";

// packages/cli/src/serve/server.ts
init_esbuild_shims();
var import_express4 = __toESM(require_express(), 1);

// packages/cli/src/serve/acp-http/index.ts
init_esbuild_shims();
import { createHash as createHash3, timingSafeEqual } from "node:crypto";
import { createRequire } from "node:module";
import * as path9 from "node:path";

// packages/cli/src/serve/acp-http/dispatch.ts
init_esbuild_shims();
import path8 from "node:path";

// packages/cli/src/serve/acp-session-bridge.ts
init_esbuild_shims();

// packages/cli/src/serve/fs/errors.ts
init_esbuild_shims();
var DEFAULT_STATUS_BY_KIND = {
  path_outside_workspace: 400,
  symlink_escape: 400,
  path_not_found: 404,
  binary_file: 422,
  file_too_large: 413,
  hash_mismatch: 409,
  file_already_exists: 409,
  text_not_found: 422,
  ambiguous_text_match: 422,
  untrusted_workspace: 403,
  permission_denied: 403,
  io_error: 503,
  internal_error: 500,
  parse_error: 400
};
var FsError = class extends Error {
  static {
    __name(this, "FsError");
  }
  kind;
  status;
  hint;
  constructor(kind, message, options) {
    super(
      message,
      options?.cause === void 0 ? void 0 : { cause: options.cause }
    );
    this.name = "FsError";
    this.kind = kind;
    this.status = options?.status ?? DEFAULT_STATUS_BY_KIND[kind];
    this.hint = options?.hint;
  }
};
function isFsError(err) {
  return err instanceof FsError;
}
__name(isFsError, "isFsError");
function wrapAsFsError(err, fallbackKind = "internal_error") {
  if (err instanceof FsError) return err;
  const errno = err?.code;
  const message = err instanceof Error ? err.message : "unknown filesystem error";
  switch (errno) {
    case "ENOENT":
      return new FsError("path_not_found", message, { cause: err });
    case "EACCES":
    case "EPERM":
      return new FsError("permission_denied", message, { cause: err });
    case "ELOOP":
      return new FsError("symlink_escape", message, {
        cause: err,
        hint: "symlink chain forms a cycle or exceeds SYMLOOP_MAX"
      });
    case "EISDIR":
      return new FsError("parse_error", message, {
        cause: err,
        hint: "EISDIR \u2014 path is a directory but a regular file was expected"
      });
    case "ENOTDIR":
      return new FsError("parse_error", message, {
        cause: err,
        hint: "ENOTDIR \u2014 a path component is a regular file but a directory was expected"
      });
    case "ENOSPC":
      return new FsError("io_error", message, {
        cause: err,
        hint: "filesystem is full (df -h reporting 100%)"
      });
    case "EIO":
      return new FsError("io_error", message, {
        cause: err,
        hint: "underlying I/O error (failing disk or kernel-level fault)"
      });
    case "EBUSY":
    case "ETXTBSY":
      return new FsError("io_error", message, {
        cause: err,
        hint: "file is busy; another process holds an exclusive handle"
      });
    case "ENAMETOOLONG":
      return new FsError("io_error", message, {
        cause: err,
        hint: "path exceeds the OS PATH_MAX"
      });
    case "EMFILE":
    case "ENFILE":
      return new FsError("io_error", message, {
        cause: err,
        hint: "too many open files; daemon is at file-descriptor limit"
      });
    default:
      return new FsError(fallbackKind, message, { cause: err });
  }
}
__name(wrapAsFsError, "wrapAsFsError");

// packages/cli/src/serve/fs/paths.ts
init_esbuild_shims();
import { promises as fsp } from "node:fs";
import * as path from "node:path";
var ENOENT_TOLERATING_INTENTS = /* @__PURE__ */ new Set([
  "write",
  "stat"
]);
function hasSuspiciousPathPattern(p) {
  if (process.platform === "win32") {
    const colonIndex = p.indexOf(":", 2);
    if (colonIndex !== -1) return true;
  }
  if (process.platform === "win32" && /~\d+/.test(p)) return true;
  if (p.startsWith("\\\\?\\") || p.startsWith("\\\\.\\") || p.startsWith("//?/") || p.startsWith("//./")) {
    return true;
  }
  if (p.startsWith("\\\\") && p.length > 2 && p[2] !== "\\" || p.startsWith("//") && p.length > 2 && p[2] !== "/") {
    return true;
  }
  if (/(^|\/|\\)\.{3,}(\/|\\|$)/.test(p)) return true;
  for (const seg of p.split(/[\\/]/)) {
    if (seg === "" || seg === "." || seg === "..") continue;
    if (/[.\s]+$/.test(seg)) return true;
    if (/(^|\.)(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.|$)/i.test(seg)) {
      return true;
    }
  }
  return false;
}
__name(hasSuspiciousPathPattern, "hasSuspiciousPathPattern");
var MAX_ANCESTOR_HOPS = 40;
var CANONICAL_BOUND_CACHE = /* @__PURE__ */ new Map();
function canonicalizeBoundWorkspaceCached(boundWorkspace) {
  const cached = CANONICAL_BOUND_CACHE.get(boundWorkspace);
  if (cached !== void 0) return cached;
  const canonical = canonicalizeWorkspace(boundWorkspace);
  CANONICAL_BOUND_CACHE.set(boundWorkspace, canonical);
  return canonical;
}
__name(canonicalizeBoundWorkspaceCached, "canonicalizeBoundWorkspaceCached");
async function findExistingAncestor(absolute) {
  let current = absolute;
  const tailParts = [];
  for (let i = 0; i < MAX_ANCESTOR_HOPS; i++) {
    let stat = null;
    try {
      stat = await fsp.stat(current);
    } catch (err) {
      const code = err?.code;
      if (code === "ENOENT" || code === "ENOTDIR") {
      } else {
        throw err;
      }
    }
    if (stat) {
      if (tailParts.length > 0 && !stat.isDirectory()) {
        throw new FsError(
          "parse_error",
          `path component is not a directory: ${absolute}`,
          {
            hint: "a non-directory file occupies a path segment"
          }
        );
      }
      return { ancestor: current, tail: tailParts.join(path.sep) };
    }
    const parent = path.dirname(current);
    if (parent === current) {
      throw new FsError(
        "path_not_found",
        `no existing ancestor for ${absolute}`
      );
    }
    tailParts.unshift(path.basename(current));
    current = parent;
  }
  throw new FsError(
    "path_not_found",
    `path traversal exceeded ${MAX_ANCESTOR_HOPS} hops while finding ancestor`,
    {
      hint: "path is too deeply nested or filesystem is responding unexpectedly"
    }
  );
}
__name(findExistingAncestor, "findExistingAncestor");
async function resolveWithinWorkspace(input, boundWorkspace, intent) {
  if (typeof input !== "string" || input.length === 0) {
    throw new FsError("parse_error", "path must be a non-empty string");
  }
  if (hasSuspiciousPathPattern(input)) {
    throw new FsError(
      "path_outside_workspace",
      `path contains suspicious pattern: ${input}`,
      {
        hint: "paths with NTFS ADS, 8.3 short names, UNC prefixes, or trailing dots are rejected outright"
      }
    );
  }
  const boundCanonical = canonicalizeBoundWorkspaceCached(boundWorkspace);
  const absolute = path.resolve(boundCanonical, input);
  if (!isWithinRoot(absolute, boundCanonical)) {
    throw new FsError(
      "path_outside_workspace",
      `path escapes workspace: ${input}`
    );
  }
  let canonical;
  try {
    canonical = await fsp.realpath(absolute);
  } catch (err) {
    const code = err?.code;
    if (code === "ENOENT" && ENOENT_TOLERATING_INTENTS.has(intent)) {
      let symlinkResolvedCanonical = null;
      try {
        let cursor = absolute;
        const visitedInodes = /* @__PURE__ */ new Set();
        let firstHopTarget = null;
        let resolvedFully = false;
        for (let hop = 0; hop < MAX_ANCESTOR_HOPS; hop++) {
          let linkStat;
          try {
            linkStat = await fsp.lstat(cursor);
          } catch (lstatErr) {
            const lcode = lstatErr?.code;
            if (lcode === "ENOENT" || lcode === "ENOTDIR") {
              resolvedFully = true;
              break;
            }
            throw lstatErr;
          }
          if (!linkStat.isSymbolicLink()) {
            resolvedFully = true;
            break;
          }
          const ino = typeof linkStat.ino === "bigint" ? linkStat.ino : BigInt(linkStat.ino);
          if (ino !== 0n && visitedInodes.has(ino)) {
            throw new FsError(
              "symlink_escape",
              `symlink cycle detected at ${cursor}`,
              { hint: "symlink loops back onto a previously visited inode" }
            );
          }
          if (ino !== 0n) visitedInodes.add(ino);
          const target = await fsp.readlink(cursor);
          const absTarget = path.isAbsolute(target) ? target : path.resolve(path.dirname(cursor), target);
          if (firstHopTarget === null) firstHopTarget = target;
          cursor = absTarget;
        }
        if (!resolvedFully) {
          throw new FsError(
            "symlink_escape",
            `symlink chain exceeded ${MAX_ANCESTOR_HOPS} hops for ${input}`,
            { hint: "symlink chain is too long or contains a cycle" }
          );
        }
        if (firstHopTarget !== null) {
          const { ancestor: targetAncestor, tail: targetTail } = await findExistingAncestor(cursor);
          const targetAncestorReal = await fsp.realpath(targetAncestor);
          const canonicalTarget = targetTail ? path.join(targetAncestorReal, targetTail) : targetAncestorReal;
          if (!isWithinRoot(canonicalTarget, boundCanonical)) {
            throw new FsError(
              "symlink_escape",
              `dangling symlink target escapes workspace: ${input}`,
              {
                // Hint must NOT embed the symlink target — `recordDenied`
                // forwards `hint` into `fs.denied` even in privacy mode,
                // and an absolute outside-target string would leak the
                // attacker's intended exfiltration path through audit
                // events. Operators wanting the actual target value
                // run with `QWEN_AUDIT_RAW_PATHS=1` and read it from
                // `relPath` / `message`.
                hint: "symlink chain leaves the workspace; enable QWEN_AUDIT_RAW_PATHS for the resolved target"
              }
            );
          }
          symlinkResolvedCanonical = canonicalTarget;
        }
      } catch (err2) {
        if (err2 instanceof FsError) throw err2;
        const code2 = err2?.code;
        if (code2 !== "ENOENT") throw err2;
      }
      if (symlinkResolvedCanonical !== null) {
        canonical = symlinkResolvedCanonical;
      } else {
        const { ancestor, tail } = await findExistingAncestor(absolute);
        const ancestorReal = await fsp.realpath(ancestor);
        canonical = tail ? path.join(ancestorReal, tail) : ancestorReal;
      }
    } else if (code === "ENOENT") {
      throw new FsError("path_not_found", `path does not exist: ${input}`, {
        cause: err
      });
    } else if (code === "ELOOP") {
      throw new FsError(
        "symlink_escape",
        `symlink loop or chain too long for ${input}`,
        {
          cause: err,
          hint: "a symlink in the path forms a cycle or exceeds SYMLOOP_MAX"
        }
      );
    } else if (code === "EACCES") {
      throw new FsError(
        "permission_denied",
        `permission denied resolving ${input}`,
        { cause: err }
      );
    } else {
      throw err;
    }
  }
  if (!isWithinRoot(canonical, boundCanonical)) {
    const kind = canonical !== absolute ? "symlink_escape" : "path_outside_workspace";
    throw new FsError(
      kind,
      kind === "symlink_escape" ? `symlink resolves outside workspace: ${input}` : `path escapes workspace: ${input}`
    );
  }
  return canonical;
}
__name(resolveWithinWorkspace, "resolveWithinWorkspace");

// packages/cli/src/serve/fs/index.ts
init_esbuild_shims();

// packages/cli/src/serve/fs/policy.ts
init_esbuild_shims();
import * as path2 from "node:path";
var MAX_READ_BYTES = 256 * 1024;
var MAX_WRITE_BYTES = 5 * 1024 * 1024;
function shouldIgnore(absolute, boundWorkspace, ignore, kind = "file") {
  const rel = path2.relative(boundWorkspace, absolute);
  if (rel === "" || rel.startsWith("..")) return { ignored: false };
  const fileFilter = ignore.getFileFilter();
  const dirFilter = ignore.getDirectoryFilter();
  if (kind === "directory") {
    const withSlash = rel.endsWith("/") ? rel : `${rel}/`;
    if (dirFilter(withSlash)) return { ignored: true, category: "directory" };
    if (fileFilter(rel)) return { ignored: true, category: "file" };
    return { ignored: false };
  }
  if (fileFilter(rel)) return { ignored: true, category: "file" };
  if (dirFilter(rel)) return { ignored: true, category: "directory" };
  return { ignored: false };
}
__name(shouldIgnore, "shouldIgnore");
function assertTrustedForIntent(trusted, intent) {
  if (trusted) return;
  switch (intent) {
    case "read":
    case "list":
    case "glob":
    case "stat":
      return;
    case "write":
    case "edit":
      throw new FsError(
        "untrusted_workspace",
        `workspace is not trusted; ${intent} operations are forbidden`,
        {
          hint: "mark the folder as trusted in the daemon configuration to allow writes"
        }
      );
    default: {
      const _exhaustive = intent;
      throw new FsError(
        "untrusted_workspace",
        `workspace is not trusted; intent "${String(_exhaustive)}" is not classified`,
        {
          hint: "unknown intent reached the trust gate; classify it in policy.ts"
        }
      );
    }
  }
}
__name(assertTrustedForIntent, "assertTrustedForIntent");
function enforceReadSize(fileBytes, maxBytes = MAX_READ_BYTES) {
  if (fileBytes <= maxBytes) {
    return { bytesToRead: fileBytes, truncated: false };
  }
  return { bytesToRead: maxBytes, truncated: true };
}
__name(enforceReadSize, "enforceReadSize");
function enforceWriteSize(bytes, maxBytes = MAX_WRITE_BYTES) {
  if (bytes > maxBytes) {
    throw new FsError(
      "file_too_large",
      `payload of ${bytes} bytes exceeds write limit of ${maxBytes} bytes`,
      {
        hint: "split the write into smaller chunks or raise the daemon limit"
      }
    );
  }
}
__name(enforceWriteSize, "enforceWriteSize");
async function detectBinary(filePath) {
  return isBinaryFile(filePath);
}
__name(detectBinary, "detectBinary");

// packages/cli/src/serve/fs/audit.ts
init_esbuild_shims();
import { createHash } from "node:crypto";
import * as path3 from "node:path";
var FS_ACCESS_EVENT_TYPE = "fs.access";
var FS_DENIED_EVENT_TYPE = "fs.denied";
function hashPath(absolute) {
  return createHash("sha256").update(absolute).digest("hex").slice(0, 16);
}
__name(hashPath, "hashPath");
var CROSS_DRIVE_RELPATH = "<cross-drive>";
function relForAudit(raw, boundWorkspace) {
  const rel = path3.isAbsolute(raw) ? path3.relative(boundWorkspace, raw) : raw;
  return path3.isAbsolute(rel) ? CROSS_DRIVE_RELPATH : rel;
}
__name(relForAudit, "relForAudit");
function rawPathsEnabled() {
  return process.env["QWEN_AUDIT_RAW_PATHS"] === "1";
}
__name(rawPathsEnabled, "rawPathsEnabled");
function createAuditPublisher(deps) {
  const includeRawPaths = deps.includeRawPaths ?? rawPathsEnabled();
  const { emit, boundWorkspace } = deps;
  return {
    recordAccess(ctx, record) {
      const absolute = String(record.absolute);
      const payload = {
        kind: FS_ACCESS_EVENT_TYPE,
        intent: record.intent,
        route: ctx.route,
        pathHash: hashPath(absolute),
        durationMs: record.durationMs
      };
      if (ctx.sessionId) payload.sessionId = ctx.sessionId;
      if (record.sizeBytes !== void 0) payload.sizeBytes = record.sizeBytes;
      if (record.truncated) payload.truncated = true;
      if (record.matchedIgnore) payload.matchedIgnore = record.matchedIgnore;
      if (record.pattern !== void 0 && includeRawPaths) {
        payload.pattern = record.pattern;
      }
      if (includeRawPaths) {
        payload.relPath = relForAudit(absolute, boundWorkspace);
      }
      emit({
        v: EVENT_SCHEMA_VERSION,
        type: FS_ACCESS_EVENT_TYPE,
        data: payload,
        originatorClientId: ctx.originatorClientId
      });
    },
    recordDenied(ctx, record) {
      const probe = path3.isAbsolute(record.input) ? record.input : path3.resolve(boundWorkspace, record.input);
      const payload = {
        kind: FS_DENIED_EVENT_TYPE,
        intent: record.intent,
        route: ctx.route,
        pathHash: hashPath(probe),
        errorKind: record.errorKind
      };
      if (ctx.sessionId) payload.sessionId = ctx.sessionId;
      if (record.hint) payload.hint = record.hint;
      if (record.message && includeRawPaths) {
        payload.message = record.message;
      }
      if (record.pattern !== void 0 && includeRawPaths) {
        payload.pattern = record.pattern;
      }
      if (includeRawPaths) {
        payload.relPath = relForAudit(record.input, boundWorkspace);
      }
      emit({
        v: EVENT_SCHEMA_VERSION,
        type: FS_DENIED_EVENT_TYPE,
        data: payload,
        originatorClientId: ctx.originatorClientId
      });
    }
  };
}
__name(createAuditPublisher, "createAuditPublisher");

// packages/cli/src/serve/fs/workspace-file-system.ts
init_esbuild_shims();
import { createHash as createHash2, randomBytes } from "node:crypto";
import { promises as fsp2 } from "node:fs";
import * as path4 from "node:path";
function createWorkspaceFileSystemFactory(deps) {
  const boundWorkspace = canonicalizeWorkspace(deps.boundWorkspace);
  const ignore = deps.ignore ?? loadIgnoreRules({
    projectRoot: boundWorkspace,
    useGitignore: true,
    useQwenignore: true,
    ...deps.customIgnoreFiles !== void 0 ? { customIgnoreFiles: deps.customIgnoreFiles } : {},
    ignoreDirs: []
  });
  Object.freeze(ignore);
  const audit = createAuditPublisher({
    emit: deps.emit,
    boundWorkspace,
    includeRawPaths: deps.includeRawPaths
  });
  const lowFs = new StandardFileSystemService();
  const pathLocks = new PathMutexRegistry();
  return {
    assertCanWrite() {
      assertTrustedForIntent(deps.trusted, "write");
    },
    forRequest(ctx) {
      return new WorkspaceFileSystemImpl({
        boundWorkspace,
        trusted: deps.trusted,
        ignore,
        audit,
        ctx,
        lowFs,
        pathLocks
      });
    }
  };
}
__name(createWorkspaceFileSystemFactory, "createWorkspaceFileSystemFactory");
var WorkspaceFileSystemImpl = class {
  constructor(deps) {
    this.deps = deps;
  }
  static {
    __name(this, "WorkspaceFileSystemImpl");
  }
  async resolve(input, intent) {
    try {
      return await resolveWithinWorkspace(
        input,
        this.deps.boundWorkspace,
        intent
      );
    } catch (err) {
      throw this.recordAndWrap(err, intent, input);
    }
  }
  async stat(p) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "stat");
      const st = await fsp2.lstat(p);
      const out = {
        kind: kindFromStatLike(st),
        sizeBytes: st.size,
        modifiedMs: st.mtimeMs
      };
      this.deps.audit.recordAccess(this.deps.ctx, {
        intent: "stat",
        absolute: p,
        durationMs: performance.now() - start,
        sizeBytes: st.size
      });
      return out;
    } catch (err) {
      throw this.recordAndWrap(err, "stat", p);
    }
  }
  async readText(p, opts = {}) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "read");
      if (opts.line !== void 0 && (!Number.isSafeInteger(opts.line) || opts.line < 1)) {
        throw new FsError(
          "parse_error",
          `line must be a positive integer, got ${opts.line}`
        );
      }
      if (opts.limit !== void 0 && (!Number.isSafeInteger(opts.limit) || opts.limit < 1)) {
        throw new FsError(
          "parse_error",
          `limit must be a positive integer, got ${opts.limit}`
        );
      }
      const snapshot = await readTextSnapshotFromResolvedFile(p, opts);
      const ignoreVerdict = shouldIgnore(
        p,
        this.deps.boundWorkspace,
        this.deps.ignore,
        "file"
      );
      const meta = snapshot.meta;
      if (ignoreVerdict.ignored) meta.matchedIgnore = ignoreVerdict.category;
      this.deps.audit.recordAccess(this.deps.ctx, {
        intent: "read",
        absolute: p,
        durationMs: performance.now() - start,
        sizeBytes: meta.sizeBytes,
        truncated: meta.truncated,
        matchedIgnore: meta.matchedIgnore
      });
      return { content: snapshot.content, meta };
    } catch (err) {
      throw this.recordAndWrap(err, "read", p);
    }
  }
  async readBytes(p, opts = {}) {
    const out = await this.readBytesWindow(p, opts);
    return out.buffer;
  }
  async readBytesWindow(p, opts = {}) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "read");
      const offset = opts.offset ?? 0;
      const maxBytes = opts.maxBytes ?? MAX_READ_BYTES;
      if (!Number.isSafeInteger(offset) || offset < 0) {
        throw new FsError(
          "parse_error",
          `offset must be a non-negative integer, got ${offset}`
        );
      }
      if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > MAX_READ_BYTES) {
        throw new FsError(
          "parse_error",
          `maxBytes must be a positive integer in [1, ${MAX_READ_BYTES}], got ${maxBytes}`
        );
      }
      const pre = await fsp2.lstat(p);
      if (!pre.isFile()) {
        throw new FsError("parse_error", `path is not a regular file: ${p}`);
      }
      const fh = await fsp2.open(p, "r");
      let st;
      let buf;
      try {
        st = await fh.stat();
        assertSameFile(pre, st, p, "read");
        const available = Math.max(0, st.size - offset);
        const toRead = Math.min(maxBytes, available);
        buf = Buffer.allocUnsafe(toRead);
        if (toRead > 0) {
          const read = await fh.read(buf, 0, toRead, offset);
          buf = read.bytesRead === toRead ? buf : buf.subarray(0, read.bytesRead);
        }
        const afterRead = await fh.stat();
        assertSameFile(st, afterRead, p, "read");
        if (afterRead.size !== st.size || afterRead.mtimeMs !== st.mtimeMs) {
          throw new FsError("hash_mismatch", `file changed during read: ${p}`, {
            hint: "retry after re-reading the latest file hash"
          });
        }
      } finally {
        await fh.close();
      }
      await assertInodeStableAfterRead(p, st.ino);
      const fullWindow = offset === 0 && buf.length === st.size;
      const out = {
        buffer: buf,
        sizeBytes: st.size,
        returnedBytes: buf.length,
        offset,
        truncated: !fullWindow,
        ...fullWindow ? { hash: hashBuffer(buf) } : {}
      };
      this.deps.audit.recordAccess(this.deps.ctx, {
        intent: "read",
        absolute: p,
        durationMs: performance.now() - start,
        sizeBytes: buf.length,
        truncated: out.truncated
      });
      return out;
    } catch (err) {
      throw this.recordAndWrap(err, "read", p);
    }
  }
  async list(p, opts = {}) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "list");
      if (opts.maxEntries !== void 0 && (!Number.isSafeInteger(opts.maxEntries) || opts.maxEntries < 1)) {
        throw new FsError(
          "parse_error",
          `maxEntries must be a positive integer, got ${opts.maxEntries}`
        );
      }
      const entries = [];
      const dir = await fsp2.opendir(p);
      for await (const d of dir) {
        const childAbs = path4.join(p, d.name);
        const kind = kindFromStatLike(d);
        const verdict = shouldIgnore(
          childAbs,
          this.deps.boundWorkspace,
          this.deps.ignore,
          kind === "directory" ? "directory" : "file"
        );
        if (verdict.ignored && !opts.includeIgnored) continue;
        entries.push({ name: d.name, kind, ignored: verdict.ignored });
        if (opts.maxEntries !== void 0 && entries.length >= opts.maxEntries) {
          break;
        }
      }
      this.deps.audit.recordAccess(this.deps.ctx, {
        intent: "list",
        absolute: p,
        durationMs: performance.now() - start,
        sizeBytes: entries.length,
        truncated: opts.maxEntries !== void 0 && entries.length >= opts.maxEntries
      });
      return entries;
    } catch (err) {
      throw this.recordAndWrap(err, "list", p);
    }
  }
  async glob(pattern, opts = {}) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "glob");
      if (pattern.split(/[\\/]/).some((seg) => seg === "..")) {
        throw new FsError(
          "parse_error",
          `glob pattern may not contain '..' segments: ${pattern}`
        );
      }
      if (path4.isAbsolute(pattern) || /^[A-Za-z]:[\\/]/.test(pattern) || pattern.startsWith("\\\\") || pattern.startsWith("//")) {
        throw new FsError(
          "parse_error",
          `glob pattern must be workspace-relative: ${pattern}`,
          { hint: 'pass a relative pattern such as "src/**/*.ts"' }
        );
      }
      const cwd = opts.cwd ?? this.deps.boundWorkspace;
      let cwdReal;
      if (cwd === this.deps.boundWorkspace) {
        cwdReal = cwd;
      } else {
        try {
          cwdReal = await fsp2.realpath(path4.resolve(cwd));
        } catch (err) {
          const code = err?.code;
          if (code === "ENOENT") {
            throw new FsError(
              "path_not_found",
              `glob cwd does not exist: ${cwd}`,
              { cause: err }
            );
          }
          throw err;
        }
      }
      if (!isWithinRoot(cwdReal, this.deps.boundWorkspace)) {
        throw new FsError(
          "path_outside_workspace",
          `glob cwd is outside workspace: ${cwd}`,
          { hint: "opts.cwd must be a path obtained from fs.resolve()" }
        );
      }
      const matches = await glob(pattern, {
        cwd: cwdReal,
        nodir: false,
        absolute: true,
        dot: true,
        ignore: ["**/node_modules/**", "**/.git/**"]
      });
      const out = [];
      const max = opts.maxResults ?? Number.POSITIVE_INFINITY;
      let escapedCount = 0;
      let permissionErrorCount = 0;
      let transientErrorCount = 0;
      for (const hit of matches) {
        if (out.length >= max) break;
        const absolute = path4.resolve(hit);
        let canonical;
        try {
          canonical = await fsp2.realpath(absolute);
        } catch (err) {
          const code = err?.code;
          if (code === "ENOENT" || code === "ELOOP") {
            escapedCount += 1;
          } else if (code === "EACCES" || code === "EPERM") {
            permissionErrorCount += 1;
          } else {
            transientErrorCount += 1;
          }
          continue;
        }
        const rel = path4.relative(this.deps.boundWorkspace, canonical);
        if (rel.startsWith("..") || path4.isAbsolute(rel)) {
          escapedCount += 1;
          continue;
        }
        let dirent = null;
        try {
          dirent = await fsp2.lstat(canonical);
        } catch {
          dirent = null;
        }
        const kind = dirent?.isDirectory() ? "directory" : "file";
        const verdict = shouldIgnore(
          canonical,
          this.deps.boundWorkspace,
          this.deps.ignore,
          kind
        );
        if (verdict.ignored && !opts.includeIgnored) continue;
        out.push(canonical);
      }
      if (escapedCount > 0) {
        this.deps.audit.recordDenied(this.deps.ctx, {
          intent: "glob",
          input: pattern,
          errorKind: "symlink_escape",
          hint: `glob filtered ${escapedCount} hit(s) that resolved outside workspace`,
          pattern
        });
      }
      if (permissionErrorCount > 0) {
        this.deps.audit.recordDenied(this.deps.ctx, {
          intent: "glob",
          input: pattern,
          errorKind: "permission_denied",
          hint: `glob skipped ${permissionErrorCount} hit(s) due to EACCES/EPERM`,
          pattern
        });
      }
      if (transientErrorCount > 0) {
        this.deps.audit.recordDenied(this.deps.ctx, {
          intent: "glob",
          input: pattern,
          // `io_error` (not `permission_denied`) so monitoring
          // pipelines that page security oncall on
          // `permission_denied` aren't woken up by a failing disk
          // or busy file. The kind was added to `FsErrorKind` for
          // exactly this case (and for `wrapAsFsError`'s ENOSPC /
          // EIO / EBUSY / ETXTBSY / ENAMETOOLONG / EMFILE / ENFILE
          // mappings).
          errorKind: "io_error",
          hint: `glob skipped ${transientErrorCount} hit(s) due to transient I/O errors (EIO/EBUSY/ENAMETOOLONG/EMFILE)`,
          pattern
        });
      }
      this.deps.audit.recordAccess(this.deps.ctx, {
        intent: "glob",
        absolute: this.deps.boundWorkspace,
        durationMs: performance.now() - start,
        sizeBytes: out.length,
        pattern
      });
      return out;
    } catch (err) {
      throw this.recordAndWrap(err, "glob", pattern);
    }
  }
  async writeTextAtomic(p, content, opts) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "write");
      validateWriteTextAtomicOptions(opts);
      const decodedSizeBytes = Buffer.byteLength(content, "utf-8");
      enforceWriteSize(decodedSizeBytes);
      const out = await this.deps.pathLocks.runExclusive(
        p,
        async () => {
          const existingMeta = opts.mode === "replace" ? await readExistingTextMeta(p, opts.expectedHash) : void 0;
          if (opts.mode === "create") {
            await assertCreateTargetAbsent(p);
          }
          const meta = mergeWriteMeta(existingMeta, opts);
          const result = await atomicWriteTextResolvedFile({
            target: p,
            content,
            mode: opts.mode,
            expectedHash: opts.expectedHash,
            meta
          });
          const verdict = shouldIgnore(
            p,
            this.deps.boundWorkspace,
            this.deps.ignore,
            "file"
          );
          if (verdict.ignored) meta.matchedIgnore = verdict.category;
          meta.sizeBytes = result.sizeBytes;
          meta.hash = result.hash;
          this.deps.audit.recordAccess(this.deps.ctx, {
            intent: "write",
            absolute: p,
            durationMs: performance.now() - start,
            sizeBytes: result.sizeBytes,
            matchedIgnore: meta.matchedIgnore
          });
          return {
            created: opts.mode === "create",
            sizeBytes: result.sizeBytes,
            hash: result.hash,
            meta
          };
        }
      );
      return out;
    } catch (err) {
      throw this.recordAndWrap(err, "write", p);
    }
  }
  async writeTextOverwrite(p, content, opts = {}) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "write");
      const decodedSizeBytes = Buffer.byteLength(content, "utf-8");
      enforceWriteSize(decodedSizeBytes);
      const out = await this.deps.pathLocks.runExclusive(
        p,
        async () => {
          let targetExisted = false;
          try {
            await fsp2.lstat(p);
            targetExisted = true;
          } catch (err) {
            if (err?.code !== "ENOENT") {
              throw err;
            }
          }
          let existingMeta;
          try {
            existingMeta = await readExistingTextMeta(p);
          } catch (err) {
            const code = err?.code;
            const kind = err?.kind;
            if (code !== "ENOENT" && code !== "EACCES" && code !== "EPERM" && kind !== "file_too_large" && kind !== "binary_file") {
              throw err;
            }
          }
          const meta = mergeWriteMeta(existingMeta, opts);
          const result = await atomicWriteTextResolvedFile({
            target: p,
            content,
            mode: "overwrite",
            meta
          });
          const verdict = shouldIgnore(
            p,
            this.deps.boundWorkspace,
            this.deps.ignore,
            "file"
          );
          if (verdict.ignored) meta.matchedIgnore = verdict.category;
          meta.sizeBytes = result.sizeBytes;
          meta.hash = result.hash;
          this.deps.audit.recordAccess(this.deps.ctx, {
            intent: "write",
            absolute: p,
            durationMs: performance.now() - start,
            sizeBytes: result.sizeBytes,
            matchedIgnore: meta.matchedIgnore
          });
          return {
            created: !targetExisted,
            sizeBytes: result.sizeBytes,
            hash: result.hash,
            meta
          };
        }
      );
      return out;
    } catch (err) {
      throw this.recordAndWrap(err, "write", p);
    }
  }
  async writeText(p, content, opts) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "write");
      const sizeBytes = Buffer.byteLength(content, "utf-8");
      enforceWriteSize(sizeBytes);
      await assertNotSymlinkBeforeWrite(p);
      await this.deps.lowFs.writeTextFile({
        path: p,
        content,
        _meta: opts ? buildWriteMeta(opts) : void 0
      });
      const verdict = shouldIgnore(
        p,
        this.deps.boundWorkspace,
        this.deps.ignore,
        "file"
      );
      this.deps.audit.recordAccess(this.deps.ctx, {
        intent: "write",
        absolute: p,
        durationMs: performance.now() - start,
        sizeBytes,
        matchedIgnore: verdict.ignored ? verdict.category : void 0
      });
    } catch (err) {
      throw this.recordAndWrap(err, "write", p);
    }
  }
  async editAtomic(p, oldText, newText, opts) {
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "edit");
      if (!isContentHash(opts.expectedHash)) {
        throw new FsError(
          "parse_error",
          "expectedHash must match sha256:<64 lowercase hex chars>"
        );
      }
      if (typeof oldText !== "string" || oldText.length === 0) {
        throw new FsError(
          "parse_error",
          `oldText must be a non-empty string for edit on ${p}`
        );
      }
      if (typeof newText !== "string") {
        throw new FsError("parse_error", "newText must be a string");
      }
      const out = await this.deps.pathLocks.runExclusive(
        p,
        async () => {
          const snapshot = await readTextSnapshotFromResolvedFile(p);
          if (snapshot.meta.hash !== opts.expectedHash) {
            throw new FsError(
              "hash_mismatch",
              `expected ${opts.expectedHash}, found ${snapshot.meta.hash}`,
              { hint: "re-read the file and retry with the latest hash" }
            );
          }
          const current = snapshot.content;
          const occurrences = countOccurrences(current, oldText);
          if (occurrences === 0) {
            const snippet = oldText.length > 80 ? oldText.slice(0, 80) + "..." : oldText;
            throw new FsError("text_not_found", `oldText not found in ${p}`, {
              hint: `searched for: ${JSON.stringify(snippet)}`
            });
          }
          if (occurrences > 1) {
            throw new FsError(
              "ambiguous_text_match",
              `oldText appears ${occurrences} times in ${p}`,
              {
                hint: "pass a larger oldText span that occurs exactly once"
              }
            );
          }
          const idx = current.indexOf(oldText);
          const next = current.slice(0, idx) + newText + current.slice(idx + oldText.length);
          enforceWriteSize(Buffer.byteLength(next, "utf-8"));
          const meta = mergeWriteMeta(snapshot.meta, {});
          const result = await atomicWriteTextResolvedFile({
            target: p,
            content: next,
            mode: "replace",
            expectedHash: opts.expectedHash,
            meta
          });
          const verdict = shouldIgnore(
            p,
            this.deps.boundWorkspace,
            this.deps.ignore,
            "file"
          );
          if (verdict.ignored) meta.matchedIgnore = verdict.category;
          meta.sizeBytes = result.sizeBytes;
          meta.hash = result.hash;
          this.deps.audit.recordAccess(this.deps.ctx, {
            intent: "edit",
            absolute: p,
            durationMs: performance.now() - start,
            sizeBytes: result.sizeBytes,
            matchedIgnore: meta.matchedIgnore
          });
          return {
            writtenBytes: result.sizeBytes,
            hash: result.hash,
            meta
          };
        }
      );
      return out;
    } catch (err) {
      throw this.recordAndWrap(err, "edit", p);
    }
  }
  async edit(p, oldText, newText, opts) {
    if (opts?.expectedHash !== void 0) {
      return this.editAtomic(p, oldText, newText, {
        expectedHash: opts.expectedHash
      });
    }
    const start = performance.now();
    try {
      assertTrustedForIntent(this.deps.trusted, "edit");
      const st = await fsp2.stat(p);
      if (st.size > MAX_READ_BYTES) {
        throw new FsError(
          "file_too_large",
          `file of ${st.size} bytes exceeds edit cap of ${MAX_READ_BYTES} bytes`,
          {
            hint: "split large edits into bounded readBytes/writeText sequences"
          }
        );
      }
      if (await detectBinary(p)) {
        throw new FsError("binary_file", `cannot edit binary file: ${p}`, {
          hint: "edit() works on text files only"
        });
      }
      if (oldText.length === 0) {
        throw new FsError(
          "parse_error",
          `oldText must be a non-empty string for edit on ${p}`,
          {
            hint: "empty oldText would match at position 0 and silently prepend newText"
          }
        );
      }
      const readResult = await this.deps.lowFs.readTextFile({
        path: p,
        limit: Number.POSITIVE_INFINITY,
        line: 0
      });
      const current = readResult.content;
      await assertInodeStableAfterRead(p, st.ino);
      const idx = current.indexOf(oldText);
      if (idx === -1) {
        const snippet = oldText.length > 80 ? oldText.slice(0, 80) + "\u2026" : oldText;
        throw new FsError("parse_error", `oldText not found in ${p}`, {
          hint: `edit() expects oldText to appear verbatim; searched for: ${JSON.stringify(snippet)}`
        });
      }
      const next = current.slice(0, idx) + newText + current.slice(idx + oldText.length);
      const writtenBytes = Buffer.byteLength(next, "utf-8");
      enforceWriteSize(writtenBytes);
      await assertNotSymlinkBeforeWrite(p);
      await this.deps.lowFs.writeTextFile({
        path: p,
        content: next,
        _meta: readResult._meta
      });
      const editVerdict = shouldIgnore(
        p,
        this.deps.boundWorkspace,
        this.deps.ignore,
        "file"
      );
      this.deps.audit.recordAccess(this.deps.ctx, {
        intent: "edit",
        absolute: p,
        durationMs: performance.now() - start,
        sizeBytes: writtenBytes,
        matchedIgnore: editVerdict.ignored ? editVerdict.category : void 0
      });
      return { writtenBytes };
    } catch (err) {
      throw this.recordAndWrap(err, "edit", p);
    }
  }
  /**
   * Coerce an arbitrary thrown value into an `FsError`, emit the
   * matching `fs.denied` audit event, and return the typed error
   * for the caller to rethrow. Body methods invoke this in their
   * `catch` so:
   *   - raw fs errnos (`EACCES`, `ENOTDIR`, …) get categorized
   *     instead of escaping as opaque 5xx,
   *   - the audit log records every failure (the prior helper
   *     early-returned for non-`FsError`s and silently lost the
   *     event), and
   *   - routes can still rely on `instanceof FsError`
   *     for their `sendFsError` serializer.
   */
  recordAndWrap(err, intent, input) {
    const fs3 = wrapAsFsError(err);
    this.deps.audit.recordDenied(this.deps.ctx, {
      intent,
      input,
      errorKind: fs3.kind,
      hint: fs3.hint,
      // Quote the underlying OS / FsError message so audit
      // consumers debugging a production incident can see the
      // actual cause (errno text, byte counts, glob pattern,
      // etc.) rather than just `errorKind` + `hint`.
      message: fs3.message,
      // For glob denials (parse_error pattern rejection,
      // catastrophic walk failures) the input IS the pattern
      // already; surfacing it on the dedicated `pattern` field
      // keeps the schema parallel with successful `recordAccess`
      // glob rows so consumers can `data.pattern` without
      // branching on intent.
      pattern: intent === "glob" ? input : void 0
    });
    return fs3;
  }
};
var CONTENT_HASH_RE = /^sha256:[0-9a-f]{64}$/;
function isContentHash(value) {
  return typeof value === "string" && CONTENT_HASH_RE.test(value);
}
__name(isContentHash, "isContentHash");
var PathMutexRegistry = class {
  static {
    __name(this, "PathMutexRegistry");
  }
  tails = /* @__PURE__ */ new Map();
  async runExclusive(key, fn) {
    const previous = this.tails.get(key) ?? Promise.resolve();
    let release;
    const current = new Promise((resolve5) => {
      release = resolve5;
    });
    const tail = previous.catch(() => void 0).then(() => current);
    this.tails.set(key, tail);
    await previous.catch(() => void 0);
    try {
      return await fn();
    } finally {
      release();
      if (this.tails.get(key) === tail) this.tails.delete(key);
    }
  }
};
function validateWriteTextAtomicOptions(opts) {
  if (opts.mode !== "create" && opts.mode !== "replace") {
    throw new FsError(
      "parse_error",
      'mode must be either "create" or "replace" (use writeTextOverwrite() for unconditional overwrites)'
    );
  }
  if (opts.expectedHash !== void 0 && !isContentHash(opts.expectedHash)) {
    throw new FsError(
      "parse_error",
      "expectedHash must match sha256:<64 lowercase hex chars>"
    );
  }
  if (opts.mode === "replace" && opts.expectedHash === void 0) {
    throw new FsError(
      "parse_error",
      'expectedHash is required when mode is "replace"'
    );
  }
  if (opts.lineEnding !== void 0 && opts.lineEnding !== "lf" && opts.lineEnding !== "crlf") {
    throw new FsError("parse_error", 'lineEnding must be "lf" or "crlf"');
  }
}
__name(validateWriteTextAtomicOptions, "validateWriteTextAtomicOptions");
async function readTextSnapshotFromResolvedFile(p, opts = {}) {
  const pre = await fsp2.lstat(p);
  if (pre.isSymbolicLink()) {
    throw new FsError("symlink_escape", `path is a symlink: ${p}`, {
      hint: "re-resolve the target file instead of reading through a link"
    });
  }
  if (!pre.isFile()) {
    throw new FsError("parse_error", `path is not a regular file: ${p}`);
  }
  if (pre.size > MAX_READ_BYTES) {
    throw new FsError(
      "file_too_large",
      `file of ${pre.size} bytes exceeds read cap of ${MAX_READ_BYTES} bytes`,
      {
        hint: "use readBytes for explicit byte-windowed access on large files"
      }
    );
  }
  const raw = await readStableRegularFileBuffer(p, pre);
  if (looksBinary(raw)) {
    throw new FsError("binary_file", `binary file: ${p}`, {
      hint: "use readBytes for binary content"
    });
  }
  const decoded = decodeBufferWithEncodingInfo(raw);
  const startLineIndex = opts.line !== void 0 ? opts.line - 1 : 0;
  const sliced = sliceDecodedText(
    decoded.content,
    startLineIndex,
    opts.limit ?? Number.POSITIVE_INFINITY
  );
  const sizeOutcome = enforceReadSize(raw.length, opts.maxBytes);
  let content = sliced.content;
  const meta = {
    encoding: decoded.encoding,
    bom: decoded.bom,
    lineEnding: detectLineEnding(content),
    sizeBytes: raw.length,
    originalLineCount: sliced.originalLineCount,
    hash: hashBuffer(raw)
  };
  if (sizeOutcome.truncated) {
    const buf = Buffer.from(content, "utf-8");
    if (buf.length > sizeOutcome.bytesToRead) {
      content = safeUtf8Truncate(buf, sizeOutcome.bytesToRead).toString(
        "utf-8"
      );
      meta.lineEnding = detectLineEnding(content);
    }
    meta.truncated = true;
  }
  if (opts.limit !== void 0 && Number.isFinite(opts.limit) && sliced.originalLineCount > opts.limit + startLineIndex) {
    meta.truncated = true;
  }
  return { content, meta };
}
__name(readTextSnapshotFromResolvedFile, "readTextSnapshotFromResolvedFile");
async function readStableRegularFileBuffer(p, pre) {
  const fh = await fsp2.open(p, "r");
  let opened;
  try {
    opened = await fh.stat();
    assertSameFile(pre, opened, p, "read");
    if (opened.size > MAX_READ_BYTES) {
      throw new FsError(
        "file_too_large",
        `file of ${opened.size} bytes exceeds read cap of ${MAX_READ_BYTES} bytes`,
        {
          hint: "use readBytes for explicit byte-windowed access on large files"
        }
      );
    }
    const out = Buffer.alloc(opened.size);
    let offset = 0;
    while (offset < opened.size) {
      const read = await fh.read(out, offset, opened.size - offset, offset);
      if (read.bytesRead === 0) break;
      offset += read.bytesRead;
    }
    const afterRead = await fh.stat();
    assertSameFile(opened, afterRead, p, "read");
    if (afterRead.size !== opened.size || afterRead.mtimeMs !== opened.mtimeMs) {
      throw new FsError("hash_mismatch", `file changed during read: ${p}`, {
        hint: "retry after re-reading the latest file hash"
      });
    }
    const post = await fsp2.lstat(p);
    assertSameFile(pre, post, p, "read");
    if (post.size !== opened.size || post.mtimeMs !== opened.mtimeMs) {
      throw new FsError("hash_mismatch", `file changed during read: ${p}`, {
        hint: "retry after re-reading the latest file hash"
      });
    }
    return offset === out.length ? out : out.subarray(0, offset);
  } finally {
    await fh.close();
  }
}
__name(readStableRegularFileBuffer, "readStableRegularFileBuffer");
function sliceDecodedText(content, startLine, limit) {
  const lines = content.split("\n");
  const originalLineCount = lines.length;
  const endLine = Math.min(startLine + limit, originalLineCount);
  const actualStartLine = Math.min(startLine, originalLineCount);
  return {
    content: lines.slice(actualStartLine, endLine).join("\n"),
    originalLineCount
  };
}
__name(sliceDecodedText, "sliceDecodedText");
function looksBinary(buf) {
  if (buf.length === 0) return false;
  const bomProbe = buf.subarray(0, Math.min(4, buf.length));
  const hasUnicodeBom = bomProbe.length >= 4 && (bomProbe[0] === 255 && bomProbe[1] === 254 && bomProbe[2] === 0 && bomProbe[3] === 0 || bomProbe[0] === 0 && bomProbe[1] === 0 && bomProbe[2] === 254 && bomProbe[3] === 255) || bomProbe.length >= 3 && bomProbe[0] === 239 && bomProbe[1] === 187 && bomProbe[2] === 191 || bomProbe.length >= 2 && (bomProbe[0] === 255 && bomProbe[1] === 254 || bomProbe[0] === 254 && bomProbe[1] === 255);
  if (hasUnicodeBom) return false;
  const sampleLength = Math.min(4096, buf.length);
  let nonPrintableCount = 0;
  for (let i = 0; i < sampleLength; i++) {
    if (buf[i] === 0) return true;
    if (buf[i] < 9 || buf[i] > 13 && buf[i] < 32) {
      nonPrintableCount++;
    }
  }
  return nonPrintableCount / sampleLength > 0.3;
}
__name(looksBinary, "looksBinary");
async function readExistingTextMeta(p, expectedHash) {
  const snapshot = await readTextSnapshotFromResolvedFile(p);
  if (expectedHash !== void 0 && snapshot.meta.hash !== expectedHash) {
    throw new FsError(
      "hash_mismatch",
      `expected ${expectedHash}, found ${snapshot.meta.hash}`,
      { hint: "re-read the file and retry with the latest hash" }
    );
  }
  return snapshot.meta;
}
__name(readExistingTextMeta, "readExistingTextMeta");
function mergeWriteMeta(existing, opts) {
  return {
    encoding: opts.encoding ?? existing?.encoding ?? "utf-8",
    bom: opts.bom ?? existing?.bom ?? false,
    lineEnding: opts.lineEnding ?? existing?.lineEnding ?? "lf"
  };
}
__name(mergeWriteMeta, "mergeWriteMeta");
async function atomicWriteTextResolvedFile(input) {
  const target = input.target;
  const parent = path4.dirname(target);
  const parentStat = await fsp2.lstat(parent);
  if (parentStat.isSymbolicLink()) {
    throw new FsError("symlink_escape", `parent path is a symlink: ${parent}`, {
      hint: "re-resolve the target after detecting parent-symlink swaps"
    });
  }
  if (!parentStat.isDirectory()) {
    throw new FsError(
      "parse_error",
      `parent path is not a directory: ${parent}`
    );
  }
  const tmpPath = path4.join(
    parent,
    `.${path4.basename(target)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`
  );
  let tempLive = false;
  let tempHandle;
  let tempStat;
  try {
    tempHandle = await reserveTempFile(tmpPath);
    tempLive = true;
    const encoded = await writeEncodedTextTemp({
      targetPath: target,
      tmpPath,
      content: input.content,
      meta: input.meta,
      handle: tempHandle
    });
    tempStat = encoded.stat;
    const targetState = await assertAtomicTargetPrecondition({
      target,
      mode: input.mode,
      expectedHash: input.expectedHash
    });
    await chmodHandleBestEffort(tempHandle, targetState.mode ?? 384);
    await assertTempPathMatchesStat(tmpPath, tempStat);
    await tempHandle.close();
    tempHandle = void 0;
    await assertTempPathMatchesStat(tmpPath, tempStat);
    if (input.mode === "create") {
      await publishCreateNoClobber(tmpPath, target);
    } else {
      await renameWithRetryLocal(tmpPath, target, 3, 50);
    }
    tempLive = false;
    await fsyncParentDirBestEffort(parent);
    return encoded;
  } catch (err) {
    await tempHandle?.close().catch(() => void 0);
    if (tempLive) {
      try {
        await fsp2.unlink(tmpPath);
      } catch {
      }
    }
    throw err;
  }
}
__name(atomicWriteTextResolvedFile, "atomicWriteTextResolvedFile");
async function reserveTempFile(tmpPath) {
  return fsp2.open(tmpPath, "wx", 384);
}
__name(reserveTempFile, "reserveTempFile");
async function writeEncodedTextTemp(input) {
  const buf = encodeTextFileContent(
    input.targetPath,
    input.content,
    buildWriteMeta(input.meta)
  );
  enforceWriteSize(buf.length);
  await input.handle.writeFile(buf);
  await syncHandleBestEffort(input.handle);
  const st = await fsp2.lstat(input.tmpPath);
  const opened = await input.handle.stat();
  assertSameFile(opened, st, input.tmpPath, "write");
  if (st.isSymbolicLink()) {
    throw new FsError(
      "symlink_escape",
      `temporary path became a symlink: ${input.tmpPath}`,
      { hint: "temp-file race detected before final rename" }
    );
  }
  if (!st.isFile()) {
    throw new FsError(
      "parse_error",
      `temporary path is not a regular file: ${input.tmpPath}`
    );
  }
  return { sizeBytes: buf.length, hash: hashBuffer(buf), stat: st };
}
__name(writeEncodedTextTemp, "writeEncodedTextTemp");
async function assertCreateTargetAbsent(target) {
  try {
    const st = await fsp2.lstat(target);
    if (st.isSymbolicLink()) {
      throw new FsError(
        "symlink_escape",
        `path is a symlink and cannot be created over: ${target}`,
        { hint: "remove the symlink or resolve the target explicitly" }
      );
    }
    throw new FsError("file_already_exists", `file already exists: ${target}`);
  } catch (err) {
    const code = err?.code;
    if (code === "ENOENT") return;
    throw err;
  }
}
__name(assertCreateTargetAbsent, "assertCreateTargetAbsent");
async function assertAtomicTargetPrecondition(input) {
  if (input.mode === "create") {
    await assertCreateTargetAbsent(input.target);
    return {};
  }
  if (input.mode === "overwrite") {
    let pre2;
    try {
      pre2 = await fsp2.lstat(input.target);
    } catch (err) {
      if (err?.code === "ENOENT") {
        return {};
      }
      throw err;
    }
    if (pre2.isSymbolicLink()) {
      throw new FsError(
        "symlink_escape",
        `path is a symlink and cannot be overwritten atomically: ${input.target}`,
        {
          hint: "re-resolve the target file instead of writing through a link"
        }
      );
    }
    if (!pre2.isFile()) {
      throw new FsError(
        "parse_error",
        `path is not a regular file: ${input.target}`
      );
    }
    return { mode: pre2.mode & 4095 };
  }
  if (!isContentHash(input.expectedHash)) {
    throw new FsError(
      "parse_error",
      'expectedHash is required when mode is "replace"'
    );
  }
  const pre = await fsp2.lstat(input.target);
  if (pre.isSymbolicLink()) {
    throw new FsError(
      "symlink_escape",
      `path is a symlink and cannot be replaced atomically: ${input.target}`,
      { hint: "re-resolve the target file instead of writing through a link" }
    );
  }
  if (!pre.isFile()) {
    throw new FsError(
      "parse_error",
      `path is not a regular file: ${input.target}`
    );
  }
  const actual = await hashRegularFileAtPath(input.target, pre);
  if (actual !== input.expectedHash) {
    throw new FsError(
      "hash_mismatch",
      `expected ${input.expectedHash}, found ${actual}`,
      { hint: "re-read the file and retry with the latest hash" }
    );
  }
  return { mode: pre.mode & 4095 };
}
__name(assertAtomicTargetPrecondition, "assertAtomicTargetPrecondition");
async function hashRegularFileAtPath(p, pre) {
  const fh = await fsp2.open(p, "r");
  const hash = createHash2("sha256");
  let opened;
  try {
    opened = await fh.stat();
    assertSameFile(pre, opened, p, "read");
    const buf = Buffer.allocUnsafe(64 * 1024);
    let offset = 0;
    while (offset < opened.size) {
      const read = await fh.read(
        buf,
        0,
        Math.min(buf.length, opened.size - offset),
        offset
      );
      if (read.bytesRead === 0) break;
      hash.update(buf.subarray(0, read.bytesRead));
      offset += read.bytesRead;
    }
  } finally {
    await fh.close();
  }
  if (opened === void 0) {
    throw new FsError("internal_error", `failed to stat opened file: ${p}`);
  }
  const post = await fsp2.lstat(p);
  assertSameFile(pre, post, p, "read");
  if (post.size !== opened.size || post.mtimeMs !== opened.mtimeMs) {
    throw new FsError("hash_mismatch", `file changed during hash: ${p}`, {
      hint: "retry after re-reading the latest file hash"
    });
  }
  return `sha256:${hash.digest("hex")}`;
}
__name(hashRegularFileAtPath, "hashRegularFileAtPath");
function hashBuffer(buf) {
  return `sha256:${createHash2("sha256").update(buf).digest("hex")}`;
}
__name(hashBuffer, "hashBuffer");
function assertSameFile(pre, post, p, intent) {
  const preDev = toBigInt(pre.dev);
  const postDev = toBigInt(post.dev);
  const preIno = toBigInt(pre.ino);
  const postIno = toBigInt(post.ino);
  if (preDev !== 0n && postDev !== 0n && preIno !== 0n && postIno !== 0n && (preDev !== postDev || preIno !== postIno)) {
    throw new FsError("symlink_escape", `path changed during ${intent}: ${p}`, {
      hint: "TOCTOU swap detected via device/inode comparison"
    });
  }
}
__name(assertSameFile, "assertSameFile");
function toBigInt(value) {
  return typeof value === "bigint" ? value : BigInt(value);
}
__name(toBigInt, "toBigInt");
async function syncHandleBestEffort(fh) {
  try {
    await fh.sync();
  } catch {
  }
}
__name(syncHandleBestEffort, "syncHandleBestEffort");
async function fsyncParentDirBestEffort(parent) {
  let fh;
  try {
    fh = await fsp2.open(parent, "r");
    await fh.sync();
  } catch {
  } finally {
    await fh?.close().catch(() => void 0);
  }
}
__name(fsyncParentDirBestEffort, "fsyncParentDirBestEffort");
async function chmodHandleBestEffort(fh, mode) {
  try {
    await fh.chmod(mode);
  } catch {
  }
}
__name(chmodHandleBestEffort, "chmodHandleBestEffort");
async function assertTempPathMatchesStat(tmpPath, expected) {
  const st = await fsp2.lstat(tmpPath);
  if (st.isSymbolicLink()) {
    throw new FsError(
      "symlink_escape",
      `temporary path is a symlink: ${tmpPath}`,
      {
        hint: "temp-file race detected before final rename"
      }
    );
  }
  if (!st.isFile()) {
    throw new FsError(
      "parse_error",
      `temporary path is not a regular file: ${tmpPath}`
    );
  }
  assertSameFile(expected, st, tmpPath, "write");
}
__name(assertTempPathMatchesStat, "assertTempPathMatchesStat");
async function publishCreateNoClobber(tmpPath, target) {
  try {
    await fsp2.link(tmpPath, target);
  } catch (err) {
    const code = err?.code;
    if (code === "EEXIST") {
      throw new FsError(
        "file_already_exists",
        `file already exists: ${target}`
      );
    }
    throw err;
  }
  await fsp2.unlink(tmpPath).catch(() => void 0);
}
__name(publishCreateNoClobber, "publishCreateNoClobber");
async function renameWithRetryLocal(src, dest, retries, delayMs) {
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      await fsp2.rename(src, dest);
      return;
    } catch (err) {
      const code = err?.code;
      const retryable = code === "EPERM" || code === "EACCES";
      if (!retryable || attempt === retries) throw err;
      await new Promise(
        (resolve5) => setTimeout(resolve5, delayMs * 2 ** attempt)
      );
    }
  }
}
__name(renameWithRetryLocal, "renameWithRetryLocal");
function countOccurrences(haystack, needle) {
  let count = 0;
  let index = 0;
  while (true) {
    const found = haystack.indexOf(needle, index);
    if (found === -1) return count;
    count += 1;
    index = found + needle.length;
  }
}
__name(countOccurrences, "countOccurrences");
function safeUtf8Truncate(buf, maxBytes) {
  if (buf.length <= maxBytes) return buf;
  let end = maxBytes;
  while (end > 0 && (buf[end] & 192) === 128) end--;
  return buf.subarray(0, end);
}
__name(safeUtf8Truncate, "safeUtf8Truncate");
async function assertInodeStableAfterRead(p, preIno) {
  const post = await fsp2.lstat(p);
  if (post.isSymbolicLink()) {
    throw new FsError(
      "symlink_escape",
      `path was replaced with a symlink during read: ${p}`,
      { hint: "TOCTOU swap detected via post-read lstat" }
    );
  }
  const preNum = toBigInt(preIno);
  const postNum = toBigInt(post.ino);
  if (preNum !== 0n && postNum !== 0n && preNum !== postNum) {
    throw new FsError(
      "symlink_escape",
      `path inode changed during read: ${p}`,
      { hint: "TOCTOU swap detected via inode comparison" }
    );
  }
}
__name(assertInodeStableAfterRead, "assertInodeStableAfterRead");
async function assertNotSymlinkBeforeWrite(p) {
  let pre;
  try {
    pre = await fsp2.lstat(p);
  } catch (err) {
    const code = err?.code;
    if (code === "ENOENT") return;
    throw err;
  }
  if (pre.isSymbolicLink()) {
    throw new FsError(
      "symlink_escape",
      `path was replaced with a symlink before write: ${p}`,
      {
        hint: "TOCTOU swap detected via pre-write lstat \u2014 re-resolve before retrying"
      }
    );
  }
}
__name(assertNotSymlinkBeforeWrite, "assertNotSymlinkBeforeWrite");
function kindFromStatLike(s) {
  if (s.isSymbolicLink()) return "symlink";
  if (s.isDirectory()) return "directory";
  if (s.isFile()) return "file";
  return "other";
}
__name(kindFromStatLike, "kindFromStatLike");
function buildWriteMeta(opts) {
  const meta = {};
  if (opts.bom !== void 0) meta["bom"] = opts.bom;
  if (opts.encoding) meta["encoding"] = opts.encoding;
  if (opts.lineEnding) meta["lineEnding"] = opts.lineEnding;
  return Object.keys(meta).length > 0 ? meta : void 0;
}
__name(buildWriteMeta, "buildWriteMeta");

// packages/cli/src/serve/routes/workspace-setup-github.ts
init_esbuild_shims();
import { promises as fsp3 } from "node:fs";
import * as path6 from "node:path";

// packages/cli/src/serve/routes/workspace-file-read.ts
init_esbuild_shims();
import * as path5 from "node:path";
var MAX_LIST_ENTRIES = 2e3;
var MAX_FILE_LINE_LIMIT = 2e3;
var DEFAULT_FILE_BYTES_MAX_BYTES = 64 * 1024;
var DEFAULT_GLOB_MAX_RESULTS = 5e3;
var MAX_GLOB_MAX_RESULTS = 5e4;
function applyReadHeaders(res) {
  res.set("Cache-Control", "no-store");
  res.set("X-Content-Type-Options", "nosniff");
}
__name(applyReadHeaders, "applyReadHeaders");
function sendFsError(res, err, route) {
  applyReadHeaders(res);
  if (isFsError(err)) {
    const fs3 = err;
    res.status(fs3.status).json({
      errorKind: fs3.kind,
      error: fs3.message,
      hint: fs3.hint,
      status: fs3.status
    });
    return;
  }
  writeStderrLine(
    `qwen serve: ${route} unexpected error: ${err instanceof Error ? err.message : String(err)}`
  );
  res.status(500).json({
    errorKind: "internal_error",
    error: err instanceof Error ? err.message : String(err),
    status: 500
  });
}
__name(sendFsError, "sendFsError");
function parseIntInRange(raw, min, max) {
  if (raw === void 0) return void 0;
  if (typeof raw !== "string" || !/^\d+$/.test(raw)) return null;
  const n = Number.parseInt(raw, 10);
  if (!Number.isSafeInteger(n) || n < min || n > max) return null;
  return n;
}
__name(parseIntInRange, "parseIntInRange");
function parseBoolFlag(raw) {
  return raw === "1" || raw === "true";
}
__name(parseBoolFlag, "parseBoolFlag");
function requireStringQuery(res, raw, name, route) {
  if (typeof raw !== "string" || raw.length === 0) {
    applyReadHeaders(res);
    res.status(400).json({
      errorKind: "parse_error",
      error: `${name} query parameter is required`,
      status: 400
    });
    writeStderrLine(
      `qwen serve: ${route} rejected request missing required ?${name}`
    );
    return null;
  }
  return raw;
}
__name(requireStringQuery, "requireStringQuery");
function getFsFactory(req, res) {
  const factory = req.app.locals.fsFactory;
  if (!factory) {
    applyReadHeaders(res);
    res.status(500).json({
      errorKind: "internal_error",
      error: "workspace filesystem factory is not configured",
      status: 500
    });
    return null;
  }
  return factory;
}
__name(getFsFactory, "getFsFactory");
async function handleGetFile(req, res, deps) {
  const ROUTE2 = "GET /file";
  const factory = getFsFactory(req, res);
  if (!factory) return;
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const queryPath = requireStringQuery(res, req.query["path"], "path", ROUTE2);
  if (queryPath === null) return;
  const maxBytes = parseIntInRange(req.query["maxBytes"], 1, 256 * 1024);
  if (maxBytes === null) {
    applyReadHeaders(res);
    res.status(400).json({
      errorKind: "parse_error",
      error: "`maxBytes` must be a positive integer in [1, 262144]",
      status: 400
    });
    return;
  }
  const line = parseIntInRange(req.query["line"], 1, Number.MAX_SAFE_INTEGER);
  if (line === null) {
    applyReadHeaders(res);
    res.status(400).json({
      errorKind: "parse_error",
      error: "`line` must be a positive integer",
      status: 400
    });
    return;
  }
  const limit = parseIntInRange(req.query["limit"], 1, MAX_FILE_LINE_LIMIT);
  if (limit === null) {
    applyReadHeaders(res);
    res.status(400).json({
      errorKind: "parse_error",
      error: `\`limit\` must be a positive integer in [1, ${MAX_FILE_LINE_LIMIT}]`,
      status: 400
    });
    return;
  }
  const fs3 = factory.forRequest({
    originatorClientId: clientId ?? void 0,
    route: ROUTE2
  });
  try {
    const resolved = await fs3.resolve(queryPath, "read");
    const out = await fs3.readText(resolved, { maxBytes, line, limit });
    const returnedBytes = Buffer.byteLength(out.content, "utf-8");
    applyReadHeaders(res);
    res.status(200).json({
      kind: "file",
      path: workspaceRelative(req, resolved),
      content: out.content,
      encoding: out.meta.encoding ?? "utf-8",
      bom: out.meta.bom === true,
      lineEnding: out.meta.lineEnding,
      sizeBytes: out.meta.sizeBytes ?? returnedBytes,
      returnedBytes,
      truncated: out.meta.truncated === true,
      hash: out.meta.hash,
      matchedIgnore: out.meta.matchedIgnore ?? null,
      originalLineCount: out.meta.originalLineCount ?? null
    });
  } catch (err) {
    sendFsError(res, err, ROUTE2);
  }
}
__name(handleGetFile, "handleGetFile");
async function handleGetFileBytes(req, res, deps) {
  const ROUTE2 = "GET /file/bytes";
  const factory = getFsFactory(req, res);
  if (!factory) return;
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const queryPath = requireStringQuery(res, req.query["path"], "path", ROUTE2);
  if (queryPath === null) return;
  const offset = parseIntInRange(
    req.query["offset"],
    0,
    Number.MAX_SAFE_INTEGER
  );
  if (offset === null) {
    applyReadHeaders(res);
    res.status(400).json({
      errorKind: "parse_error",
      error: "`offset` must be a non-negative safe integer",
      status: 400
    });
    return;
  }
  const maxBytes = parseIntInRange(req.query["maxBytes"], 1, MAX_READ_BYTES);
  if (maxBytes === null) {
    applyReadHeaders(res);
    res.status(400).json({
      errorKind: "parse_error",
      error: `\`maxBytes\` must be a positive integer in [1, ${MAX_READ_BYTES}]`,
      status: 400
    });
    return;
  }
  const fs3 = factory.forRequest({
    originatorClientId: clientId ?? void 0,
    route: ROUTE2
  });
  try {
    const resolved = await fs3.resolve(queryPath, "read");
    const out = await fs3.readBytesWindow(resolved, {
      offset: offset ?? 0,
      maxBytes: maxBytes ?? DEFAULT_FILE_BYTES_MAX_BYTES
    });
    applyReadHeaders(res);
    res.status(200).json({
      kind: "file_bytes",
      path: workspaceRelative(req, resolved),
      offset: out.offset,
      sizeBytes: out.sizeBytes,
      returnedBytes: out.returnedBytes,
      truncated: out.truncated,
      contentBase64: out.buffer.toString("base64"),
      ...out.hash ? { hash: out.hash } : {}
    });
  } catch (err) {
    sendFsError(res, err, ROUTE2);
  }
}
__name(handleGetFileBytes, "handleGetFileBytes");
async function handleGetStat(req, res, deps) {
  const ROUTE2 = "GET /stat";
  const factory = getFsFactory(req, res);
  if (!factory) return;
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const queryPath = requireStringQuery(res, req.query["path"], "path", ROUTE2);
  if (queryPath === null) return;
  const fs3 = factory.forRequest({
    originatorClientId: clientId ?? void 0,
    route: ROUTE2
  });
  try {
    const resolved = await fs3.resolve(queryPath, "stat");
    const st = await fs3.stat(resolved);
    applyReadHeaders(res);
    res.status(200).json({
      kind: "stat",
      path: workspaceRelative(req, resolved),
      type: st.kind,
      sizeBytes: st.sizeBytes,
      modifiedMs: st.modifiedMs
    });
  } catch (err) {
    sendFsError(res, err, ROUTE2);
  }
}
__name(handleGetStat, "handleGetStat");
async function handleGetList(req, res, deps) {
  const ROUTE2 = "GET /list";
  const factory = getFsFactory(req, res);
  if (!factory) return;
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const queryPath = requireStringQuery(res, req.query["path"], "path", ROUTE2);
  if (queryPath === null) return;
  const includeIgnored = parseBoolFlag(req.query["includeIgnored"]);
  const fs3 = factory.forRequest({
    originatorClientId: clientId ?? void 0,
    route: ROUTE2
  });
  try {
    const resolved = await fs3.resolve(queryPath, "list");
    const entries = await fs3.list(resolved, {
      includeIgnored,
      maxEntries: MAX_LIST_ENTRIES + 1
    });
    let truncated = false;
    let returned = entries;
    if (returned.length > MAX_LIST_ENTRIES) {
      returned = returned.slice(0, MAX_LIST_ENTRIES);
      truncated = true;
    }
    applyReadHeaders(res);
    res.status(200).json({
      kind: "list",
      path: workspaceRelative(req, resolved),
      entries: returned,
      truncated,
      matchedIgnore: null
    });
  } catch (err) {
    sendFsError(res, err, ROUTE2);
  }
}
__name(handleGetList, "handleGetList");
async function handleGetGlob(req, res, deps) {
  const ROUTE2 = "GET /glob";
  const factory = getFsFactory(req, res);
  if (!factory) return;
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const pattern = requireStringQuery(
    res,
    req.query["pattern"],
    "pattern",
    ROUTE2
  );
  if (pattern === null) return;
  const includeIgnored = parseBoolFlag(req.query["includeIgnored"]);
  const maxResults = parseIntInRange(
    req.query["maxResults"],
    1,
    MAX_GLOB_MAX_RESULTS
  );
  if (maxResults === null) {
    applyReadHeaders(res);
    res.status(400).json({
      errorKind: "parse_error",
      error: `\`maxResults\` must be a positive integer in [1, ${MAX_GLOB_MAX_RESULTS}]`,
      status: 400
    });
    return;
  }
  const cwdRaw = req.query["cwd"];
  const cwdString = typeof cwdRaw === "string" && cwdRaw.length > 0 ? cwdRaw : void 0;
  const fs3 = factory.forRequest({
    originatorClientId: clientId ?? void 0,
    route: ROUTE2
  });
  const start = performance.now();
  try {
    const cwdResolved = cwdString ? await fs3.resolve(cwdString, "list") : void 0;
    const cap = maxResults ?? DEFAULT_GLOB_MAX_RESULTS;
    const probe = await fs3.glob(pattern, {
      cwd: cwdResolved,
      includeIgnored,
      maxResults: cap + 1
    });
    const truncated = probe.length > cap;
    const trimmed = truncated ? probe.slice(0, cap) : probe;
    const relMatches = trimmed.map((m) => workspaceRelative(req, m));
    applyReadHeaders(res);
    res.status(200).json({
      kind: "glob",
      pattern,
      cwd: cwdString ?? "",
      matches: relMatches,
      count: relMatches.length,
      truncated,
      durationMs: Math.round(performance.now() - start)
    });
  } catch (err) {
    sendFsError(res, err, ROUTE2);
  }
}
__name(handleGetGlob, "handleGetGlob");
function workspaceRelative(req, resolved) {
  const boundWorkspace = req.app.locals.boundWorkspace;
  if (!boundWorkspace) {
    throw new Error("bound workspace is not configured");
  }
  const rel = path5.relative(boundWorkspace, resolved);
  if (rel === "") return ".";
  return path5.sep === "/" ? rel : rel.split(path5.sep).join("/");
}
__name(workspaceRelative, "workspaceRelative");
function registerWorkspaceFileReadRoutes(app, deps) {
  app.get("/file", (req, res) => handleGetFile(req, res, deps));
  app.get("/file/bytes", (req, res) => handleGetFileBytes(req, res, deps));
  app.get("/stat", (req, res) => handleGetStat(req, res, deps));
  app.get("/list", (req, res) => handleGetList(req, res, deps));
  app.get("/glob", (req, res) => handleGetGlob(req, res, deps));
}
__name(registerWorkspaceFileReadRoutes, "registerWorkspaceFileReadRoutes");

// packages/cli/src/serve/routes/workspace-setup-github.ts
var ROUTE = "POST /workspace/setup-github";
function registerWorkspaceSetupGithubRoutes(app, deps) {
  app.post(
    "/workspace/setup-github",
    deps.mutate({ strict: true }),
    (req, res) => handleSetupGithub(req, res, deps)
  );
}
__name(registerWorkspaceSetupGithubRoutes, "registerWorkspaceSetupGithubRoutes");
async function handleSetupGithub(req, res, deps) {
  const factory = getFsFactory2(req, res);
  if (!factory) return;
  const body = deps.safeBody(req);
  if (body["consent"] !== true) {
    applyReadHeaders(res);
    res.status(400).json({
      error: "`consent` must be true",
      code: "github_setup_consent_required",
      status: 400
    });
    return;
  }
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const originatorClientId = validateClientId(clientId, deps, res);
  if (originatorClientId === null) return;
  try {
    const result = await setupGithub({
      cwd: deps.boundWorkspace,
      workspaceRoot: deps.boundWorkspace,
      proxy: resolveSetupGithubProxy(deps.boundWorkspace),
      abortSignal: requestAbortSignal(req, res),
      fileOps: createSetupGithubFileOps(factory, ROUTE, originatorClientId)
    });
    deps.bridge.publishWorkspaceEvent({
      type: "github_setup_completed",
      data: setupGithubEventData(result),
      ...originatorClientId ? { originatorClientId } : {}
    });
    applyReadHeaders(res);
    res.status(200).json(result);
  } catch (error2) {
    sendSetupGithubError(res, error2, deps.boundWorkspace);
  }
}
__name(handleSetupGithub, "handleSetupGithub");
function getFsFactory2(req, res) {
  const factory = req.app.locals.fsFactory;
  if (!factory) {
    applyReadHeaders(res);
    res.status(500).json({
      error: "workspace filesystem factory is not configured",
      code: "internal_error",
      status: 500
    });
    return null;
  }
  return factory;
}
__name(getFsFactory2, "getFsFactory");
function validateClientId(clientId, deps, res) {
  if (clientId === void 0) return void 0;
  if (!deps.bridge.knownClientIds().has(clientId)) {
    applyReadHeaders(res);
    res.status(400).json({
      error: `Client id "${clientId}" is not registered for this workspace`,
      code: "invalid_client_id",
      clientId
    });
    return null;
  }
  return clientId;
}
__name(validateClientId, "validateClientId");
function createSetupGithubFileOps(factory, route, originatorClientId) {
  const fs3 = factory.forRequest({
    route,
    ...originatorClientId ? { originatorClientId } : {}
  });
  return {
    assertCanWrite() {
      try {
        factory.assertCanWrite();
      } catch (error2) {
        throw new SetupGithubError(
          "github_setup_untrusted_workspace",
          error2 instanceof Error ? error2.message : "workspace is not trusted; write operations are forbidden",
          403
        );
      }
    },
    async ensureWorkflowDirectory(gitRepoRoot) {
      await ensureDirectoryWithoutSymlink(gitRepoRoot, [
        ".github",
        "workflows"
      ]);
    },
    async writeTextFile(_gitRepoRoot, relativePath, content) {
      const resolved = await fs3.resolve(relativePath, "write");
      const out = await fs3.writeTextOverwrite(resolved, content);
      return { sizeBytes: out.sizeBytes };
    },
    async readTextFile(_gitRepoRoot, relativePath) {
      try {
        const resolved = await fs3.resolve(relativePath, "read");
        const out = await fs3.readText(resolved);
        return out.content;
      } catch (error2) {
        if (isFsError(error2) && error2.kind === "path_not_found") {
          return void 0;
        }
        throw error2;
      }
    }
  };
}
__name(createSetupGithubFileOps, "createSetupGithubFileOps");
async function ensureDirectoryWithoutSymlink(root, segments) {
  await assertDirectoryWithoutSymlink(root, "Repository root");
  let current = root;
  const checkedSegments = [];
  for (const segment of segments) {
    current = path6.join(current, segment);
    checkedSegments.push(segment);
    const label = `Workspace path "${checkedSegments.join("/")}"`;
    try {
      const stat = await fsp3.lstat(current);
      if (stat.isSymbolicLink()) {
        throw new SetupGithubError(
          "github_setup_invalid_workspace",
          `${label} must not be a symlink.`,
          400
        );
      }
      if (!stat.isDirectory()) {
        throw new SetupGithubError(
          "github_setup_invalid_workspace",
          `${label} must be a directory.`,
          400
        );
      }
    } catch (error2) {
      if (error2 instanceof SetupGithubError) throw error2;
      if (error2.code !== "ENOENT") throw error2;
      try {
        await fsp3.mkdir(current, { mode: 493 });
      } catch (mkdirError) {
        if (mkdirError.code !== "EEXIST") {
          throw mkdirError;
        }
      }
      const postStat = await fsp3.lstat(current);
      if (postStat.isSymbolicLink()) {
        throw new SetupGithubError(
          "github_setup_invalid_workspace",
          `${label} must not be a symlink.`,
          400
        );
      }
      if (!postStat.isDirectory()) {
        throw new SetupGithubError(
          "github_setup_invalid_workspace",
          `${label} must be a directory.`,
          400
        );
      }
    }
  }
}
__name(ensureDirectoryWithoutSymlink, "ensureDirectoryWithoutSymlink");
async function assertDirectoryWithoutSymlink(target, label) {
  const stat = await fsp3.lstat(target);
  if (stat.isSymbolicLink()) {
    throw new SetupGithubError(
      "github_setup_invalid_workspace",
      `${label} must not be a symlink.`,
      400
    );
  }
  if (!stat.isDirectory()) {
    throw new SetupGithubError(
      "github_setup_invalid_workspace",
      `${label} must be a directory.`,
      400
    );
  }
}
__name(assertDirectoryWithoutSymlink, "assertDirectoryWithoutSymlink");
function sanitizeSetupGithubMessage(message, boundWorkspace) {
  return message.split(boundWorkspace).join("<workspace>");
}
__name(sanitizeSetupGithubMessage, "sanitizeSetupGithubMessage");
function sanitizeSetupGithubResult(result, boundWorkspace) {
  return {
    ...result,
    workflows: result.workflows.map((workflow) => ({
      ...workflow,
      ...workflow.error ? {
        error: sanitizeSetupGithubMessage(workflow.error, boundWorkspace)
      } : {}
    })),
    gitignore: {
      ...result.gitignore,
      ...result.gitignore.error ? {
        error: sanitizeSetupGithubMessage(
          result.gitignore.error,
          boundWorkspace
        )
      } : {}
    }
  };
}
__name(sanitizeSetupGithubResult, "sanitizeSetupGithubResult");
function sendSetupGithubError(res, error2, boundWorkspace) {
  applyReadHeaders(res);
  if (error2 instanceof SetupGithubError) {
    res.status(error2.status).json({
      error: sanitizeSetupGithubMessage(error2.message, boundWorkspace),
      code: error2.code,
      status: error2.status,
      ...error2.partial ? {
        partial: true,
        result: error2.partialResult ? sanitizeSetupGithubResult(error2.partialResult, boundWorkspace) : null
      } : {}
    });
    return;
  }
  writeStderrLine(
    `[setup-github] unexpected error: ${error2 instanceof Error ? error2.message : String(error2)}`
  );
  res.status(500).json({
    error: "An internal error occurred during GitHub setup.",
    code: "github_setup_failed",
    status: 500
  });
}
__name(sendSetupGithubError, "sendSetupGithubError");
function setupGithubEventData(result) {
  return {
    releaseTag: result.releaseTag,
    readmeUrl: result.readmeUrl,
    ...result.secretsUrl ? { secretsUrl: result.secretsUrl } : {},
    workflows: result.workflows,
    gitignore: result.gitignore,
    warnings: result.warnings
  };
}
__name(setupGithubEventData, "setupGithubEventData");
function resolveSetupGithubProxy(boundWorkspace) {
  const settings = loadSettings(boundWorkspace, { skipLoadEnvironment: true });
  const trustState = getWorkspaceTrustStatus(
    settingsForSetupGithubTrust(settings),
    boundWorkspace
  ).effective.state;
  const settingsProxy = trustState === "trusted" ? settings.merged.proxy : settings.user.settings.proxy || settings.system.settings.proxy || settings.systemDefaults.settings.proxy;
  return settingsProxy || process.env["HTTPS_PROXY"] || process.env["https_proxy"] || process.env["HTTP_PROXY"] || process.env["http_proxy"];
}
__name(resolveSetupGithubProxy, "resolveSetupGithubProxy");
function settingsForSetupGithubTrust(settings) {
  const userFolderTrust = settings.user.settings.security?.folderTrust;
  const systemFolderTrust = settings.system.settings.security?.folderTrust;
  if (!userFolderTrust && !systemFolderTrust) return {};
  return {
    security: {
      folderTrust: {
        ...systemFolderTrust,
        ...userFolderTrust
      }
    }
  };
}
__name(settingsForSetupGithubTrust, "settingsForSetupGithubTrust");
function requestAbortSignal(req, res) {
  const controller = new AbortController();
  const abort = /* @__PURE__ */ __name(() => controller.abort(), "abort");
  req.once("aborted", abort);
  res.once("close", () => {
    if (!res.writableEnded) abort();
  });
  return controller.signal;
}
__name(requestAbortSignal, "requestAbortSignal");

// packages/cli/src/serve/routes/workspace-voice.ts
init_esbuild_shims();
var import_express = __toESM(require_express(), 1);

// packages/cli/src/serve/validation-limits.ts
init_esbuild_shims();
var MAX_TRUST_REASON_LENGTH = 1024;
var MAX_VOICE_LANGUAGE_LENGTH = 1024;
var MAX_VOICE_MODEL_LENGTH = 256;

// packages/cli/src/serve/routes/workspace-voice.ts
function sendVoiceError(res, err) {
  if (err instanceof WorkspaceVoiceError) {
    res.status(err.status).json({ error: err.message, code: err.code });
    return true;
  }
  return false;
}
__name(sendVoiceError, "sendVoiceError");
function broadcastVoiceWrite(deps, write, clientId) {
  try {
    deps.broadcastSettingsChanged(
      write.key,
      write.value,
      voiceSettingsScopeToWire(write.scope),
      clientId
    );
  } catch (err) {
    writeStderrLine(
      `qwen serve: POST /workspace/voice broadcast error (key=${write.key}, scope=${voiceSettingsScopeToWire(write.scope)}): ${err instanceof Error ? err.message : String(err)}`
    );
  }
}
__name(broadcastVoiceWrite, "broadcastVoiceWrite");
function parseWorkspaceVoiceUpdateParams(body) {
  const parsed = {};
  if ("enabled" in body) {
    if (typeof body["enabled"] !== "boolean") {
      return { error: "`enabled` must be a boolean", code: "invalid_enabled" };
    }
    parsed.enabled = body["enabled"];
  }
  if ("mode" in body) {
    if (!isVoiceMode(body["mode"])) {
      return {
        error: '`mode` must be either "hold" or "tap"',
        code: "invalid_voice_mode"
      };
    }
    parsed.mode = body["mode"];
  }
  if ("language" in body) {
    if (typeof body["language"] !== "string") {
      return {
        error: "`language` must be a string",
        code: "invalid_voice_language"
      };
    }
    const language = body["language"].trim();
    if (language.length > MAX_VOICE_LANGUAGE_LENGTH) {
      return {
        error: `\`language\` exceeds the ${MAX_VOICE_LANGUAGE_LENGTH}-character limit`,
        code: "invalid_voice_language"
      };
    }
    parsed.language = language;
  }
  if ("voiceModel" in body) {
    if (typeof body["voiceModel"] !== "string") {
      return {
        error: "`voiceModel` must be a non-empty string",
        code: "invalid_voice_model"
      };
    }
    const voiceModel = body["voiceModel"].trim();
    if (!voiceModel) {
      return {
        error: "`voiceModel` must be a non-empty string",
        code: "invalid_voice_model"
      };
    }
    if (voiceModel.length > MAX_VOICE_MODEL_LENGTH) {
      return {
        error: `\`voiceModel\` exceeds the ${MAX_VOICE_MODEL_LENGTH}-character limit`,
        code: "invalid_voice_model"
      };
    }
    parsed.voiceModel = voiceModel;
  }
  if (Object.keys(parsed).length === 0) {
    return {
      error: EMPTY_WORKSPACE_VOICE_UPDATE_ERROR,
      code: "invalid_voice_update"
    };
  }
  return parsed;
}
__name(parseWorkspaceVoiceUpdateParams, "parseWorkspaceVoiceUpdateParams");
async function persistVoiceUpdate(deps, settings, update, clientId, workspaceTrusted) {
  if (!deps.persistSettings && !deps.persistSetting) {
    throw new Error("workspace voice settings persistence is not available");
  }
  const writes = buildWorkspaceVoiceSettingsWrites(settings, update, {
    workspaceTrusted
  });
  if (deps.persistSettings) {
    try {
      await deps.persistSettings(deps.boundWorkspace, writes);
    } catch (err) {
      if (err instanceof WorkspaceSettingsPartialPersistError) {
        for (const write of err.committedWrites) {
          broadcastVoiceWrite(deps, write, clientId);
        }
      }
      throw err;
    }
    for (const write of writes) {
      broadcastVoiceWrite(deps, write, clientId);
    }
  } else {
    const committed = [];
    for (const write of writes) {
      try {
        await deps.persistSetting(
          deps.boundWorkspace,
          write.scope,
          write.key,
          write.value
        );
      } catch (err) {
        writeStderrLine(
          `qwen serve: POST /workspace/voice partial persist error (workspace=${deps.boundWorkspace}, committed=${committed.length}/${writes.length}, failedKey=${write.key}, failedScope=${voiceSettingsScopeToWire(write.scope)}): ${err instanceof Error ? err.message : String(err)}`
        );
        throw new WorkspaceSettingsPartialPersistError(
          `Voice settings partial persist failed: committed=${committed.length}/${writes.length}`,
          committed,
          err
        );
      }
      committed.push(write);
    }
    for (const write of committed) {
      broadcastVoiceWrite(deps, write, clientId);
    }
  }
}
__name(persistVoiceUpdate, "persistVoiceUpdate");
function normalizeContentType(req) {
  return req.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
}
__name(normalizeContentType, "normalizeContentType");
function requestAbortSignal2(req, res) {
  const controller = new AbortController();
  const abort = /* @__PURE__ */ __name(() => controller.abort(), "abort");
  req.once("aborted", abort);
  res.once("close", () => {
    if (!res.writableEnded) abort();
  });
  return controller.signal;
}
__name(requestAbortSignal2, "requestAbortSignal");
function isSupportedAudioContentType(contentType) {
  return typeof contentType === "string" && (contentType.startsWith("audio/") || contentType === "application/octet-stream");
}
__name(isSupportedAudioContentType, "isSupportedAudioContentType");
function readBinaryBody(req) {
  const body = req.body;
  if (body instanceof Uint8Array || Buffer.isBuffer(body)) {
    return body;
  }
  if (ArrayBuffer.isView(body)) {
    return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
  }
  return void 0;
}
__name(readBinaryBody, "readBinaryBody");
function registerWorkspaceVoiceRoutes(app, deps) {
  const transcribe = deps.transcribe ?? transcribeWorkspaceVoiceAudio;
  app.get("/workspace/voice", (_req, res) => {
    try {
      res.status(200).json(
        buildWorkspaceVoiceStatus(
          deps.boundWorkspace,
          loadSettings(deps.boundWorkspace)
        )
      );
    } catch (err) {
      writeStderrLine(
        `qwen serve: GET /workspace/voice error: ${err instanceof Error ? err.message : String(err)}`
      );
      res.status(500).json({
        error: "Failed to load voice settings",
        code: "internal_error"
      });
    }
  });
  app.post(
    "/workspace/voice",
    deps.mutate({ strict: true }),
    async (req, res) => {
      if (!deps.persistSettings && !deps.persistSetting) {
        res.status(501).json({
          error: "Workspace voice settings persistence is not available",
          code: "not_implemented"
        });
        return;
      }
      const parsed = parseWorkspaceVoiceUpdateParams(deps.safeBody(req));
      if ("error" in parsed) {
        res.status(400).json(parsed);
        return;
      }
      const settings = loadSettings(deps.boundWorkspace);
      try {
        validateWorkspaceVoiceState(settings, parsed);
      } catch (err) {
        if (sendVoiceError(res, err)) return;
        throw err;
      }
      const clientId = deps.parseAndValidateClientId(req, res);
      if (clientId === null) return;
      try {
        const workspaceTrusted = getWorkspaceTrustStatus(settings.merged, deps.boundWorkspace).effective.state === "trusted";
        await persistVoiceUpdate(
          deps,
          settings,
          parsed,
          clientId,
          workspaceTrusted
        );
      } catch (err) {
        writeStderrLine(
          `qwen serve: POST /workspace/voice persist error (workspace=${deps.boundWorkspace}): ${err instanceof Error ? err.message : String(err)}`
        );
        res.status(500).json({
          error: "Failed to persist voice settings",
          code: "persist_error"
        });
        return;
      }
      res.status(200).json(
        buildWorkspaceVoiceStatus(
          deps.boundWorkspace,
          loadSettings(deps.boundWorkspace)
        )
      );
    }
  );
  app.post(
    "/workspace/voice/transcribe",
    deps.mutate({ strict: true }),
    import_express.default.raw({
      type: /* @__PURE__ */ __name((req) => isSupportedAudioContentType(
        req.headers["content-type"]?.split(";", 1)[0]?.trim().toLowerCase()
      ), "type"),
      limit: "10mb"
    }),
    async (req, res) => {
      const contentType = normalizeContentType(req);
      if (!isSupportedAudioContentType(contentType)) {
        res.status(415).json({
          error: "Content-Type must be audio/* or application/octet-stream for voice transcription",
          code: "unsupported_voice_content_type"
        });
        return;
      }
      const audioContentType = contentType;
      const clientId = deps.parseAndValidateClientId(req, res);
      if (clientId === null) return;
      const data = readBinaryBody(req);
      if (!data || data.byteLength === 0) {
        res.status(400).json({
          error: "Voice audio body must be non-empty binary data",
          code: "invalid_voice_audio"
        });
        return;
      }
      const settings = loadSettings(deps.boundWorkspace);
      if (!isVoiceEnabled(settings)) {
        res.status(403).json({
          error: "Voice transcription is disabled for this workspace",
          code: "voice_disabled"
        });
        return;
      }
      const queryVoiceModel = req.query["voiceModel"];
      if (queryVoiceModel !== void 0 && typeof queryVoiceModel !== "string") {
        res.status(400).json({
          error: "`voiceModel` query parameter must be a string",
          code: "invalid_voice_model"
        });
        return;
      }
      const requestedVoiceModel = typeof queryVoiceModel === "string" ? queryVoiceModel.trim() : "";
      if (requestedVoiceModel && requestedVoiceModel.length > MAX_VOICE_MODEL_LENGTH) {
        res.status(400).json({
          error: `\`voiceModel\` exceeds the ${MAX_VOICE_MODEL_LENGTH}-character limit`,
          code: "invalid_voice_model"
        });
        return;
      }
      const voiceModel = requestedVoiceModel || readVoiceModel(settings);
      if (!voiceModel) {
        res.status(400).json({
          error: "A valid voiceModel is required before transcription.",
          code: "voice_model_required"
        });
        return;
      }
      try {
        const result = await transcribe({
          data,
          mimeType: audioContentType,
          voiceModel,
          settings,
          workspaceCwd: deps.boundWorkspace,
          abortSignal: requestAbortSignal2(req, res)
        });
        res.status(200).json({ v: 1, ...result });
      } catch (err) {
        if (sendVoiceError(res, err)) return;
        const message = err instanceof Error ? sanitizeVoiceErrorMessage(err.message) : sanitizeVoiceErrorMessage(String(err));
        writeStderrLine(
          `qwen serve: POST /workspace/voice/transcribe error (workspace=${deps.boundWorkspace}): ${message}`
        );
        res.status(502).json({
          error: "Voice transcription failed",
          code: "voice_transcription_failed"
        });
      }
    }
  );
}
__name(registerWorkspaceVoiceRoutes, "registerWorkspaceVoiceRoutes");

// packages/cli/src/serve/workspace-memory.ts
init_esbuild_shims();
import { promises as fs } from "node:fs";
import * as path7 from "node:path";

// packages/cli/src/serve/debug-mode.ts
init_esbuild_shims();
function isServeDebugMode() {
  const value = process.env["QWEN_SERVE_DEBUG"];
  if (!value) return false;
  return !["0", "false", "off", "no"].includes(value.trim().toLowerCase());
}
__name(isServeDebugMode, "isServeDebugMode");

// packages/cli/src/serve/workspace-memory.ts
var MAX_MEMORY_CONTENT_BYTES = 1024 * 1024;
function mountWorkspaceMemoryRoutes(app, deps) {
  app.get("/workspace/memory", async (_req, res) => {
    try {
      const collectStatus = deps.collectStatus ?? collectWorkspaceMemoryStatus;
      const status = await collectStatus(deps.boundWorkspace);
      res.status(200).json(status);
    } catch (err) {
      writeStderrLine(
        `qwen serve: GET /workspace/memory failed: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
      );
      res.status(500).json({
        error: "Failed to discover workspace memory",
        code: "memory_discovery_failed"
      });
    }
  });
  app.post(
    "/workspace/memory",
    deps.mutate({ strict: true }),
    async (req, res) => {
      const body = deps.safeBody(req);
      const scope = body["scope"];
      if (scope !== "workspace" && scope !== "global") {
        res.status(400).json({
          error: '`scope` must be "workspace" or "global"',
          code: "invalid_scope"
        });
        return;
      }
      const modeRaw = body["mode"];
      if (modeRaw !== void 0 && modeRaw !== "append" && modeRaw !== "replace") {
        res.status(400).json({
          error: '`mode` must be "append", "replace", or omitted',
          code: "invalid_mode"
        });
        return;
      }
      const mode = modeRaw === "replace" ? "replace" : "append";
      const content = body["content"];
      if (typeof content !== "string") {
        res.status(400).json({
          error: "`content` must be a string",
          code: "invalid_content"
        });
        return;
      }
      if (Buffer.byteLength(content, "utf8") > MAX_MEMORY_CONTENT_BYTES) {
        res.status(400).json({
          error: `\`content\` exceeds the ${MAX_MEMORY_CONTENT_BYTES}-byte limit`,
          code: "content_too_large"
        });
        return;
      }
      const clientId = deps.parseClientId(req, res);
      if (clientId === null) return;
      let originatorClientId;
      if (clientId !== void 0) {
        const known = deps.bridge.knownClientIds();
        if (!known.has(clientId)) {
          res.status(400).json({
            error: `Client id "${clientId}" is not registered for this workspace`,
            code: "invalid_client_id",
            clientId
          });
          return;
        }
        originatorClientId = clientId;
      }
      try {
        const result = await writeWorkspaceContextFile({
          scope,
          mode,
          content,
          projectRoot: deps.boundWorkspace
        });
        const responseBody = {
          ok: true,
          filePath: result.filePath,
          bytesWritten: result.bytesWritten,
          mode,
          changed: result.changed
        };
        if (result.changed) {
          deps.bridge.publishWorkspaceEvent({
            type: "memory_changed",
            data: {
              scope,
              filePath: result.filePath,
              mode,
              bytesWritten: result.bytesWritten
            },
            ...originatorClientId ? { originatorClientId } : {}
          });
        }
        res.status(200).json(responseBody);
      } catch (err) {
        if (err instanceof WorkspaceMemoryWriteTimeoutError) {
          writeStderrLine(
            `qwen serve: POST /workspace/memory timeout \u2014 file lock at ${err.filePath} did not acquire within ${err.timeoutMs}ms (stalled FS / OneDrive / NFS)`
          );
          const debug2 = isServeDebugMode();
          res.status(500).json({
            error: debug2 ? err.message : "Workspace memory write timed out waiting for the per-file lock. Retry or restart the daemon.",
            code: "memory_write_timeout",
            scope,
            mode,
            timeoutMs: err.timeoutMs,
            ...debug2 ? { filePath: err.filePath } : {}
          });
          return;
        }
        if (err instanceof WorkspaceMemoryFileTooLargeError) {
          writeStderrLine(
            `qwen serve: POST /workspace/memory refused \u2014 existing file ${err.filePath} is ${err.bytes} bytes (cap ${err.limit})`
          );
          const debug2 = isServeDebugMode();
          res.status(413).json({
            error: debug2 ? err.message : "Existing memory file exceeds the safe-append cap. Trim the file or POST with mode=replace.",
            code: "memory_file_too_large",
            scope,
            mode,
            ...debug2 ? { filePath: err.filePath } : {},
            bytes: err.bytes,
            limit: err.limit
          });
          return;
        }
        writeStderrLine(
          `qwen serve: POST /workspace/memory failed (scope=${scope} mode=${mode}): ${err instanceof Error ? err.stack ?? err.message : String(err)}`
        );
        const osCode = err && typeof err === "object" && "code" in err ? err.code : void 0;
        const debug = isServeDebugMode();
        res.status(500).json({
          error: "Failed to write workspace memory",
          code: "file_error",
          scope,
          mode,
          ...typeof osCode === "string" ? { osCode } : {},
          ...debug ? {
            errorMessage: err instanceof Error ? err.message : String(err)
          } : {}
        });
      }
    }
  );
}
__name(mountWorkspaceMemoryRoutes, "mountWorkspaceMemoryRoutes");
async function collectWorkspaceMemoryStatus(boundWorkspace) {
  const filenames = new Set(getAllGeminiMdFilenames());
  const files = [];
  const errors = [];
  const workspaceFiles = await walkWorkspaceForMemory(
    boundWorkspace,
    filenames,
    errors
  );
  files.push(...workspaceFiles);
  const globalDir = Storage.getGlobalQwenDir();
  for (const filename of filenames) {
    const candidate = path7.join(globalDir, filename);
    try {
      const stat = await fs.stat(candidate);
      if (stat.isFile()) {
        files.push({
          absolutePath: candidate,
          scope: "global",
          bytes: stat.size
        });
      }
    } catch (err) {
      if (!isEnoent(err)) {
        errors.push({
          kind: "memory_file",
          status: "error",
          error: err instanceof Error ? err.message : String(err),
          errorKind: "stat_failed",
          hint: candidate
        });
      }
    }
  }
  if (files.length === 0 && errors.length === 0) {
    return createIdleWorkspaceMemoryStatus(boundWorkspace);
  }
  const totalBytes = files.reduce((acc, f) => acc + f.bytes, 0);
  const result = {
    v: STATUS_SCHEMA_VERSION,
    workspaceCwd: boundWorkspace,
    initialized: true,
    files: files.map(
      (f) => ({
        kind: "memory_file",
        path: f.absolutePath,
        scope: f.scope,
        bytes: f.bytes
      })
    ),
    totalBytes,
    fileCount: files.length,
    ruleCount: 0
  };
  if (errors.length > 0) result.errors = errors;
  return result;
}
__name(collectWorkspaceMemoryStatus, "collectWorkspaceMemoryStatus");
async function walkWorkspaceForMemory(start, filenames, errors) {
  const out = [];
  for (const filename of filenames) {
    const candidate = path7.join(start, filename);
    try {
      const stat = await fs.stat(candidate);
      if (stat.isFile()) {
        out.push({
          absolutePath: candidate,
          scope: "workspace",
          bytes: stat.size
        });
      }
    } catch (err) {
      if (!isEnoent(err)) {
        errors.push({
          kind: "memory_file",
          status: "error",
          error: err instanceof Error ? err.message : String(err),
          errorKind: "stat_failed",
          hint: candidate
        });
      }
    }
  }
  return out;
}
__name(walkWorkspaceForMemory, "walkWorkspaceForMemory");
function isEnoent(err) {
  return typeof err === "object" && err !== null && err.code === "ENOENT";
}
__name(isEnoent, "isEnoent");

// packages/cli/src/serve/workspace-agents.ts
init_esbuild_shims();
import { promises as fs2 } from "node:fs";
var AGENT_TYPE_PATTERN = /^[\p{L}\p{N}_-]+$/u;
var AGENT_TYPE_MAX_LENGTH = 64;
var AGENT_TYPE_MIN_LENGTH = 2;
var MAX_DESCRIPTION_BYTES = 256 * 1024;
var MAX_SYSTEM_PROMPT_BYTES = 256 * 1024;
var MAX_TOOLS_ENTRIES = 256;
var MAX_TOOL_ID_LENGTH = 256;
function mountWorkspaceAgentsRoutes(app, deps) {
  const manager = createDaemonSubagentManager(deps.boundWorkspace);
  app.get("/workspace/agents", async (_req, res) => {
    try {
      const agents = await manager.listSubagents({ force: true });
      const status = {
        v: STATUS_SCHEMA_VERSION,
        workspaceCwd: deps.boundWorkspace,
        agents: agents.map(toSummary)
      };
      res.status(200).json(status);
    } catch (err) {
      writeStderrLine(
        `qwen serve: GET /workspace/agents failed: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
      );
      res.status(500).json({
        error: "Failed to list workspace agents",
        code: "agent_list_failed"
      });
    }
  });
  app.post(
    "/workspace/agents",
    deps.mutate({ strict: true }),
    async (req, res) => {
      const body = deps.safeBody(req);
      const clientIdResult = resolveOriginatorClientId(deps, req, res);
      if (clientIdResult === null) return;
      const originatorClientId = clientIdResult;
      const scope = body["scope"];
      if (scope !== "workspace" && scope !== "global") {
        res.status(400).json({
          error: '`scope` must be "workspace" or "global"',
          code: "invalid_scope"
        });
        return;
      }
      const level = scope === "workspace" ? "project" : "user";
      const config = parseAgentConfig(body, level, res);
      if (!config) return;
      const collision = await manager.loadSubagent(config.name, level);
      if (collision) {
        res.status(409).json({
          error: `Subagent "${config.name}" already exists at ${level} level`,
          code: "agent_already_exists",
          name: config.name,
          level
        });
        return;
      }
      try {
        await manager.createSubagent(config, { level });
      } catch (err) {
        if (err instanceof SubagentError) {
          if (err.code === SubagentErrorCode.ALREADY_EXISTS) {
            res.status(409).json({
              error: err.message,
              code: "agent_already_exists",
              name: err.subagentName ?? config.name
            });
            return;
          }
          if (err.code === SubagentErrorCode.VALIDATION_ERROR || err.code === SubagentErrorCode.INVALID_CONFIG || err.code === SubagentErrorCode.INVALID_NAME || err.code === SubagentErrorCode.TOOL_NOT_FOUND) {
            res.status(422).json({
              error: err.message,
              code: "invalid_config",
              name: err.subagentName ?? config.name
            });
            return;
          }
          if (err.code === SubagentErrorCode.FILE_ERROR) {
            const debug = isServeDebugMode();
            res.status(500).json({
              error: debug ? err.message : "Failed to write workspace agent file",
              code: "file_error",
              name: err.subagentName ?? config.name
            });
            return;
          }
        }
        writeStderrLine(
          `qwen serve: POST /workspace/agents failed: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
        );
        res.status(500).json({
          error: "Failed to create workspace agent",
          code: "agent_create_failed"
        });
        return;
      }
      const created = await manager.loadSubagent(config.name, level);
      if (!created) {
        writeStderrLine(
          `qwen serve: agent_create_reload_failed (name=${safeLogValue(config.name)} level=${level}) \u2014 file likely persisted on disk; check \`GET /workspace/agents\` for a phantom entry`
        );
        res.status(500).json({
          error: "Agent creation succeeded but reload failed",
          code: "agent_create_reload_failed",
          name: config.name,
          level
        });
        return;
      }
      deps.bridge.publishWorkspaceEvent({
        type: "agent_changed",
        data: { change: "created", name: config.name, level },
        ...originatorClientId ? { originatorClientId } : {}
      });
      res.status(201).json({ ok: true, agent: toDetail(created) });
    }
  );
  app.post(
    "/workspace/agents/generate",
    deps.mutate({ strict: true }),
    async (req, res) => {
      const body = deps.safeBody(req);
      const clientIdResult = resolveOriginatorClientId(deps, req, res);
      if (clientIdResult === null) return;
      const originatorClientId = clientIdResult;
      const description = body["description"];
      if (typeof description !== "string" || description.trim().length === 0) {
        res.status(400).json({
          error: "`description` must be a non-empty string",
          code: "invalid_description"
        });
        return;
      }
      if (Buffer.byteLength(description, "utf8") > 4096) {
        res.status(400).json({
          error: "`description` exceeds the 4096-byte limit",
          code: "invalid_description"
        });
        return;
      }
      try {
        const generated = await deps.bridge.generateWorkspaceAgent(
          description.trim(),
          originatorClientId
        );
        res.status(200).json(generated);
      } catch (err) {
        writeStderrLine(
          `qwen serve: POST /workspace/agents/generate failed: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
        );
        res.status(500).json({
          error: "Failed to generate workspace agent",
          code: "agent_generate_failed"
        });
      }
    }
  );
  app.get("/workspace/agents/:agentType", async (req, res) => {
    const agentType = validateAgentType(req, res);
    if (agentType === null) return;
    try {
      const config = await manager.loadSubagent(agentType);
      if (!config) {
        res.status(404).json({
          error: `Subagent "${agentType}" not found`,
          code: "agent_not_found",
          name: agentType
        });
        return;
      }
      res.status(200).json(toDetail(config));
    } catch (err) {
      writeStderrLine(
        `qwen serve: GET /workspace/agents/${safeLogValue(agentType)} failed: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
      );
      res.status(500).json({
        error: "Failed to read workspace agent",
        code: "agent_read_failed"
      });
    }
  });
  app.post(
    "/workspace/agents/:agentType",
    deps.mutate({ strict: true }),
    async (req, res) => {
      const agentType = validateAgentType(req, res);
      if (agentType === null) return;
      const clientIdResult = resolveOriginatorClientId(deps, req, res);
      if (clientIdResult === null) return;
      const originatorClientId = clientIdResult;
      const body = deps.safeBody(req);
      const updates = parseAgentUpdates(body, res);
      if (!updates) return;
      const preferredLevel = parseScopeQuery(req, res);
      if (preferredLevel === null) return;
      const existing = await manager.loadSubagent(agentType, preferredLevel);
      if (!existing) {
        res.status(404).json({
          error: `Subagent "${agentType}" not found`,
          code: "agent_not_found",
          name: agentType
        });
        return;
      }
      if (assertMutableLevel(existing, agentType, res)) {
        return;
      }
      if (Object.keys(updates).length === 0) {
        res.status(400).json({
          error: "`POST /workspace/agents/:agentType` requires at least one updatable field in the body",
          code: "invalid_config",
          name: agentType
        });
        return;
      }
      if (isNoOpUpdate(existing, updates)) {
        res.status(200).json({
          ok: true,
          agent: toDetail(existing),
          changed: false
        });
        return;
      }
      try {
        await manager.updateSubagent(agentType, updates, existing.level);
      } catch (err) {
        if (err instanceof SubagentError) {
          if (err.code === SubagentErrorCode.NOT_FOUND) {
            res.status(404).json({
              error: err.message,
              code: "agent_not_found",
              name: err.subagentName ?? agentType
            });
            return;
          }
          if (err.code === SubagentErrorCode.INVALID_CONFIG) {
            res.status(403).json({
              error: err.message,
              code: "agent_readonly",
              name: err.subagentName ?? agentType
            });
            return;
          }
          if (err.code === SubagentErrorCode.VALIDATION_ERROR || err.code === SubagentErrorCode.INVALID_NAME || err.code === SubagentErrorCode.TOOL_NOT_FOUND) {
            res.status(422).json({
              error: err.message,
              code: "invalid_config",
              name: err.subagentName ?? agentType
            });
            return;
          }
          if (err.code === SubagentErrorCode.FILE_ERROR) {
            const debug = isServeDebugMode();
            res.status(500).json({
              error: debug ? err.message : "Failed to write workspace agent file",
              code: "file_error",
              name: err.subagentName ?? agentType
            });
            return;
          }
        }
        writeStderrLine(
          `qwen serve: POST /workspace/agents/${safeLogValue(agentType)} failed: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
        );
        res.status(500).json({
          error: "Failed to update workspace agent",
          code: "agent_update_failed"
        });
        return;
      }
      const updated = await manager.loadSubagent(agentType, existing.level);
      if (!updated) {
        writeStderrLine(
          `qwen serve: agent_update_reload_failed (name=${safeLogValue(agentType)} level=${existing.level}) \u2014 disk write completed; check \`GET /workspace/agents/${safeLogValue(agentType)}\` for the new state`
        );
        res.status(500).json({
          error: "Agent update succeeded but reload failed",
          code: "agent_update_reload_failed",
          name: agentType,
          level: existing.level
        });
        return;
      }
      const eventLevel = existing.level === "project" ? "project" : "user";
      deps.bridge.publishWorkspaceEvent({
        type: "agent_changed",
        data: { change: "updated", name: existing.name, level: eventLevel },
        ...originatorClientId ? { originatorClientId } : {}
      });
      res.status(200).json({ ok: true, agent: toDetail(updated), changed: true });
    }
  );
  app.delete(
    "/workspace/agents/:agentType",
    deps.mutate({ strict: true }),
    async (req, res) => {
      const agentType = validateAgentType(req, res);
      if (agentType === null) return;
      const clientIdResult = resolveOriginatorClientId(deps, req, res);
      if (clientIdResult === null) return;
      const originatorClientId = clientIdResult;
      const scopedLevel = parseScopeQuery(req, res);
      if (scopedLevel === null) return;
      const levelsToCheck = scopedLevel ? [scopedLevel] : ["project", "user"];
      const existingAtLevels = [];
      for (const lvl of levelsToCheck) {
        const found = await manager.loadSubagent(agentType, lvl);
        if (found) existingAtLevels.push(found);
      }
      for (const found of existingAtLevels) {
        if (assertMutableLevel(found, agentType, res)) return;
      }
      try {
        await manager.deleteSubagent(agentType, scopedLevel);
      } catch (err) {
        if (err instanceof SubagentError) {
          if (err.code === SubagentErrorCode.NOT_FOUND) {
            res.status(404).json({
              error: err.message,
              code: "agent_not_found",
              name: err.subagentName ?? agentType
            });
            return;
          }
          if (err.code === SubagentErrorCode.INVALID_CONFIG) {
            res.status(403).json({
              error: err.message,
              code: "agent_readonly",
              name: err.subagentName ?? agentType
            });
            return;
          }
        }
        writeStderrLine(
          `qwen serve: DELETE /workspace/agents/${safeLogValue(agentType)} failed: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
        );
        res.status(500).json({
          error: "Failed to delete workspace agent",
          code: "agent_delete_failed"
        });
        return;
      }
      const removed = [];
      const remaining = [];
      for (const found of existingAtLevels) {
        if (!found.filePath) {
          removed.push(found);
          continue;
        }
        try {
          await fs2.access(found.filePath);
          remaining.push(found);
        } catch {
          removed.push(found);
        }
      }
      if (remaining.length > 0) {
        writeStderrLine(
          `qwen serve: DELETE /workspace/agents/${safeLogValue(agentType)} partial \u2014 removed=${removed.map((r) => r.level).join(",") || "none"} remaining=${remaining.map((r) => `${r.level}:${r.filePath}`).join(",")}`
        );
        for (const found of removed) {
          const evtLevel = found.level === "project" ? "project" : "user";
          deps.bridge.publishWorkspaceEvent({
            type: "agent_changed",
            data: {
              change: "deleted",
              name: found.name,
              level: evtLevel
            },
            ...originatorClientId ? { originatorClientId } : {}
          });
        }
        res.status(500).json({
          error: `Failed to delete every level of subagent "${agentType}" \u2014 ${remaining.length} level(s) still have their file on disk`,
          code: "agent_delete_partial",
          name: agentType,
          removedLevels: removed.map((r) => r.level),
          remainingLevels: remaining.map((r) => r.level)
        });
        return;
      }
      if (existingAtLevels.length === 0) {
        const fallbackLevel = scopedLevel === "user" ? "user" : "project";
        deps.bridge.publishWorkspaceEvent({
          type: "agent_changed",
          data: {
            change: "deleted",
            name: agentType,
            level: fallbackLevel
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
      } else {
        for (const found of removed) {
          const evtLevel = found.level === "project" ? "project" : "user";
          deps.bridge.publishWorkspaceEvent({
            type: "agent_changed",
            data: {
              change: "deleted",
              name: found.name,
              level: evtLevel
            },
            ...originatorClientId ? { originatorClientId } : {}
          });
        }
      }
      res.status(204).end();
    }
  );
}
__name(mountWorkspaceAgentsRoutes, "mountWorkspaceAgentsRoutes");
function validateAgentType(req, res) {
  const raw = req.params["agentType"];
  if (!raw || raw.length === 0) {
    res.status(400).json({
      error: "`agentType` path parameter is required",
      code: "invalid_agent_type"
    });
    return null;
  }
  if (raw.length > AGENT_TYPE_MAX_LENGTH || !AGENT_TYPE_PATTERN.test(raw)) {
    res.status(400).json({
      error: "`agentType` must contain only letters, numbers, hyphens, or underscores (max 64 chars)",
      code: "invalid_agent_type",
      agentType: raw
    });
    return null;
  }
  return raw;
}
__name(validateAgentType, "validateAgentType");
function parseScopeQuery(req, res) {
  const raw = req.query["scope"];
  if (raw === void 0) return void 0;
  if (typeof raw !== "string") {
    res.status(400).json({
      error: '`scope` query must be a single "workspace" or "global" value',
      code: "invalid_scope"
    });
    return null;
  }
  if (raw !== "workspace" && raw !== "global") {
    res.status(400).json({
      error: '`scope` query must be "workspace" or "global"',
      code: "invalid_scope"
    });
    return null;
  }
  return raw === "workspace" ? "project" : "user";
}
__name(parseScopeQuery, "parseScopeQuery");
function assertMutableLevel(found, agentType, res) {
  if (found.isBuiltin || found.level === "builtin" || found.level === "extension" || found.level === "session") {
    res.status(403).json({
      error: `Cannot modify ${found.level}-level subagent "${agentType}"`,
      code: "agent_readonly",
      name: found.name,
      level: found.level
    });
    return true;
  }
  return false;
}
__name(assertMutableLevel, "assertMutableLevel");
function resolveOriginatorClientId(deps, req, res) {
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return null;
  if (clientId === void 0) return void 0;
  if (!deps.bridge.knownClientIds().has(clientId)) {
    res.status(400).json({
      error: `Client id "${clientId}" is not registered for this workspace`,
      code: "invalid_client_id",
      clientId
    });
    return null;
  }
  return clientId;
}
__name(resolveOriginatorClientId, "resolveOriginatorClientId");
function parseAgentConfig(body, level, res) {
  const rawName = body["name"];
  if (typeof rawName !== "string" || rawName.trim().length === 0) {
    res.status(422).json({
      error: "`name` is required and must be a non-empty string",
      code: "invalid_config"
    });
    return void 0;
  }
  const name = rawName.trim();
  if (name.length < AGENT_TYPE_MIN_LENGTH || name.length > AGENT_TYPE_MAX_LENGTH || !AGENT_TYPE_PATTERN.test(name)) {
    res.status(422).json({
      error: `\`name\` must be ${AGENT_TYPE_MIN_LENGTH}-${AGENT_TYPE_MAX_LENGTH} characters of letters, numbers, hyphens, or underscores`,
      code: "invalid_config",
      name
    });
    return void 0;
  }
  if (BuiltinAgentRegistry.isBuiltinAgent(name)) {
    res.status(422).json({
      error: `"${name}" shadows a built-in subagent and cannot be used as a project- or user-level agent name. Choose a different name.`,
      code: "invalid_config",
      name
    });
    return void 0;
  }
  const description = body["description"];
  if (typeof description !== "string" || description.trim().length === 0) {
    res.status(422).json({
      error: "`description` is required and must be a non-empty string",
      code: "invalid_config"
    });
    return void 0;
  }
  if (Buffer.byteLength(description, "utf8") > MAX_DESCRIPTION_BYTES) {
    res.status(422).json({
      error: `\`description\` exceeds the ${MAX_DESCRIPTION_BYTES}-byte limit`,
      code: "invalid_config"
    });
    return void 0;
  }
  const systemPrompt = body["systemPrompt"];
  if (typeof systemPrompt !== "string" || systemPrompt.trim().length === 0) {
    res.status(422).json({
      error: "`systemPrompt` is required and must be a non-empty string (whitespace only is rejected)",
      code: "invalid_config"
    });
    return void 0;
  }
  if (Buffer.byteLength(systemPrompt, "utf8") > MAX_SYSTEM_PROMPT_BYTES) {
    res.status(422).json({
      error: `\`systemPrompt\` exceeds the ${MAX_SYSTEM_PROMPT_BYTES}-byte limit`,
      code: "invalid_config"
    });
    return void 0;
  }
  const tools = parseStringArray(body["tools"], "tools", res);
  if (tools === null) return void 0;
  const disallowedTools = parseStringArray(
    body["disallowedTools"],
    "disallowedTools",
    res
  );
  if (disallowedTools === null) return void 0;
  const config = {
    name,
    description,
    systemPrompt,
    level
  };
  if (tools !== void 0) config.tools = tools;
  if (disallowedTools !== void 0) config.disallowedTools = disallowedTools;
  if (rejectIfPresentWrongType(body, "model", "string", res)) return void 0;
  if (typeof body["model"] === "string") config.model = body["model"];
  if (rejectIfPresentWrongType(body, "color", "string", res)) return void 0;
  if (typeof body["color"] === "string") config.color = body["color"];
  if (rejectIfPresentWrongType(body, "approvalMode", "string", res)) {
    return void 0;
  }
  if (typeof body["approvalMode"] === "string") {
    if (!APPROVAL_MODES.includes(body["approvalMode"])) {
      res.status(422).json({
        error: `\`approvalMode\` must be one of ${JSON.stringify(APPROVAL_MODES)}`,
        code: "invalid_config"
      });
      return void 0;
    }
    config.approvalMode = body["approvalMode"];
  }
  if (rejectIfPresentWrongType(body, "background", "boolean", res)) {
    return void 0;
  }
  if (typeof body["background"] === "boolean") {
    config.background = body["background"];
  }
  const runConfig = body["runConfig"];
  if (runConfig !== void 0) {
    const sanitized = sanitizeRunConfig(runConfig, res);
    if (sanitized === null) return void 0;
    config.runConfig = sanitized;
  }
  return config;
}
__name(parseAgentConfig, "parseAgentConfig");
function parseAgentUpdates(body, res) {
  const updates = {};
  if ("description" in body) {
    const value = body["description"];
    if (typeof value !== "string" || value.trim().length === 0) {
      res.status(422).json({
        error: "`description` must be a non-empty string (whitespace only is rejected) when provided",
        code: "invalid_config"
      });
      return void 0;
    }
    if (Buffer.byteLength(value, "utf8") > MAX_DESCRIPTION_BYTES) {
      res.status(422).json({
        error: `\`description\` exceeds the ${MAX_DESCRIPTION_BYTES}-byte limit`,
        code: "invalid_config"
      });
      return void 0;
    }
    updates.description = value;
  }
  if ("systemPrompt" in body) {
    const value = body["systemPrompt"];
    if (typeof value !== "string" || value.trim().length === 0) {
      res.status(422).json({
        error: "`systemPrompt` must be a non-empty string (whitespace only is rejected) when provided",
        code: "invalid_config"
      });
      return void 0;
    }
    if (Buffer.byteLength(value, "utf8") > MAX_SYSTEM_PROMPT_BYTES) {
      res.status(422).json({
        error: `\`systemPrompt\` exceeds the ${MAX_SYSTEM_PROMPT_BYTES}-byte limit`,
        code: "invalid_config"
      });
      return void 0;
    }
    updates.systemPrompt = value;
  }
  if ("tools" in body) {
    const tools = parseStringArray(body["tools"], "tools", res);
    if (tools === null) return void 0;
    if (tools !== void 0) updates.tools = tools;
  }
  if ("disallowedTools" in body) {
    const disallowedTools = parseStringArray(
      body["disallowedTools"],
      "disallowedTools",
      res
    );
    if (disallowedTools === null) return void 0;
    if (disallowedTools !== void 0) {
      updates.disallowedTools = disallowedTools;
    }
  }
  if (rejectIfPresentWrongType(body, "model", "string", res)) return void 0;
  if (typeof body["model"] === "string") updates.model = body["model"];
  if (rejectIfPresentWrongType(body, "color", "string", res)) return void 0;
  if (typeof body["color"] === "string") updates.color = body["color"];
  if (rejectIfPresentWrongType(body, "approvalMode", "string", res)) {
    return void 0;
  }
  if (typeof body["approvalMode"] === "string") {
    if (!APPROVAL_MODES.includes(body["approvalMode"])) {
      res.status(422).json({
        error: `\`approvalMode\` must be one of ${JSON.stringify(APPROVAL_MODES)}`,
        code: "invalid_config"
      });
      return void 0;
    }
    updates.approvalMode = body["approvalMode"];
  }
  if (rejectIfPresentWrongType(body, "background", "boolean", res)) {
    return void 0;
  }
  if (typeof body["background"] === "boolean") {
    updates.background = body["background"];
  }
  if ("runConfig" in body) {
    const sanitized = sanitizeRunConfig(body["runConfig"], res);
    if (sanitized === null) return void 0;
    updates.runConfig = sanitized;
  }
  return updates;
}
__name(parseAgentUpdates, "parseAgentUpdates");
function parseStringArray(value, field, res) {
  if (value === void 0) return void 0;
  if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
    res.status(422).json({
      error: `\`${field}\` must be an array of strings when provided`,
      code: "invalid_config"
    });
    return null;
  }
  if (value.length > MAX_TOOLS_ENTRIES) {
    res.status(422).json({
      error: `\`${field}\` exceeds the ${MAX_TOOLS_ENTRIES}-entry limit`,
      code: "invalid_config"
    });
    return null;
  }
  if (value.some((v) => v.length > MAX_TOOL_ID_LENGTH)) {
    res.status(422).json({
      error: `\`${field}\` entries must be at most ${MAX_TOOL_ID_LENGTH} characters`,
      code: "invalid_config"
    });
    return null;
  }
  return value;
}
__name(parseStringArray, "parseStringArray");
function rejectIfPresentWrongType(body, key, expected, res) {
  if (!(key in body)) return false;
  if (typeof body[key] === expected) return false;
  res.status(422).json({
    error: `\`${key}\` must be a ${expected} when provided`,
    code: "invalid_config"
  });
  return true;
}
__name(rejectIfPresentWrongType, "rejectIfPresentWrongType");
function isNoOpUpdate(existing, updates) {
  if (updates.description !== void 0 && updates.description !== existing.description) {
    return false;
  }
  if (updates.systemPrompt !== void 0 && updates.systemPrompt !== existing.systemPrompt) {
    return false;
  }
  if (updates.tools !== void 0 && !shallowArrayEqual(updates.tools, existing.tools)) {
    return false;
  }
  if (updates.disallowedTools !== void 0 && !shallowArrayEqual(updates.disallowedTools, existing.disallowedTools)) {
    return false;
  }
  if (updates.model !== void 0 && updates.model !== existing.model) {
    return false;
  }
  if (updates.color !== void 0 && updates.color !== existing.color) {
    return false;
  }
  if (updates.approvalMode !== void 0 && updates.approvalMode !== existing.approvalMode) {
    return false;
  }
  if (updates.background !== void 0 && updates.background !== existing.background) {
    return false;
  }
  if (updates.runConfig !== void 0) {
    const e = existing.runConfig ?? {};
    const u = updates.runConfig;
    if ("max_time_minutes" in u) {
      if (u["max_time_minutes"] !== e["max_time_minutes"]) return false;
    }
    if ("max_turns" in u) {
      if (u["max_turns"] !== e["max_turns"]) return false;
    }
  }
  return true;
}
__name(isNoOpUpdate, "isNoOpUpdate");
function shallowArrayEqual(a, b) {
  if (a === b) return true;
  if (!a || !b) return false;
  if (a.length !== b.length) return false;
  for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
  return true;
}
__name(shallowArrayEqual, "shallowArrayEqual");
function sanitizeRunConfig(raw, res) {
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
    res.status(422).json({
      error: "`runConfig` must be an object when provided",
      code: "invalid_config"
    });
    return null;
  }
  const input = raw;
  const out = {};
  if ("max_time_minutes" in input) {
    const v = input["max_time_minutes"];
    if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) {
      res.status(422).json({
        error: "`runConfig.max_time_minutes` must be a positive finite number when provided",
        code: "invalid_config"
      });
      return null;
    }
    out["max_time_minutes"] = v;
  }
  if ("max_turns" in input) {
    const v = input["max_turns"];
    if (typeof v !== "number" || !Number.isFinite(v) || v <= 0 || !Number.isInteger(v)) {
      res.status(422).json({
        error: "`runConfig.max_turns` must be a positive integer when provided",
        code: "invalid_config"
      });
      return null;
    }
    out["max_turns"] = v;
  }
  return out;
}
__name(sanitizeRunConfig, "sanitizeRunConfig");
function toSummary(config) {
  const summary = {
    kind: "agent",
    name: config.name,
    description: config.description,
    level: config.level,
    isBuiltin: config.isBuiltin === true || config.level === "builtin",
    hasTools: Array.isArray(config.tools) && config.tools.length > 0
  };
  if (config.model) summary.model = config.model;
  if (config.color) summary.color = config.color;
  if (config.background !== void 0) summary.background = config.background;
  if (config.approvalMode) summary.approvalMode = config.approvalMode;
  if (config.extensionName) summary.extensionName = config.extensionName;
  if (config.filePath) summary.filePath = config.filePath;
  return summary;
}
__name(toSummary, "toSummary");
function toDetail(config) {
  const detail = {
    ...toSummary(config),
    systemPrompt: config.systemPrompt
  };
  if (config.tools) detail.tools = [...config.tools];
  if (config.disallowedTools) {
    detail.disallowedTools = [...config.disallowedTools];
  }
  if (config.runConfig) {
    const runConfig = {};
    if (typeof config.runConfig.max_time_minutes === "number") {
      runConfig.max_time_minutes = config.runConfig.max_time_minutes;
    }
    if (typeof config.runConfig.max_turns === "number") {
      runConfig.max_turns = config.runConfig.max_turns;
    }
    detail.runConfig = runConfig;
  }
  return detail;
}
__name(toDetail, "toDetail");
function createDaemonSubagentManager(boundWorkspace, safeMode = false) {
  const stub = {
    getSdkMode: /* @__PURE__ */ __name(() => false, "getSdkMode"),
    getProjectRoot: /* @__PURE__ */ __name(() => boundWorkspace, "getProjectRoot"),
    getActiveExtensions: /* @__PURE__ */ __name(() => [], "getActiveExtensions"),
    isSafeMode: /* @__PURE__ */ __name(() => safeMode, "isSafeMode")
  };
  const guarded = new Proxy(stub, {
    get(target, prop) {
      if (prop in target) {
        return target[prop];
      }
      if (prop === "then") return void 0;
      throw new Error(
        `qwen serve workspace agents: SubagentManager touched Config.${String(prop)} which the daemon stub does not implement. Add it to createDaemonSubagentManager and audit safety.`
      );
    },
    // Mirror the `get` trap. Without a `has` trap, a SubagentManager
    // path that does `if ('someMethod' in this.config)` would consult
    // `Reflect.has(target, prop)` directly and silently return false
    // for unimplemented methods — bypassing the throw the `get` trap
    // is supposed to surface. With the trap, an `in` check on an
    // unknown method throws the same way a property access would, so
    // both code paths behave consistently.
    has(target, prop) {
      if (prop in target) return true;
      if (prop === "then") return false;
      throw new Error(
        `qwen serve workspace agents: SubagentManager probed Config.${String(prop)} via 'in' check; the daemon stub does not implement it. Add it to createDaemonSubagentManager and audit safety.`
      );
    }
  });
  return new SubagentManager(guarded);
}
__name(createDaemonSubagentManager, "createDaemonSubagentManager");

// packages/cli/src/serve/server/session-archive.ts
init_esbuild_shims();
var SessionArchiveCoordinator = class {
  static {
    __name(this, "SessionArchiveCoordinator");
  }
  exclusive = /* @__PURE__ */ new Set();
  shared = /* @__PURE__ */ new Map();
  assertNotTransitioning(sessionId) {
    if (this.exclusive.has(sessionId)) {
      throw new SessionArchivingError(sessionId);
    }
  }
  async runExclusiveMany(sessionIds, fn) {
    const uniqueSessionIds = [...new Set(sessionIds)];
    for (const sessionId of uniqueSessionIds) {
      this.assertNotTransitioning(sessionId);
      if ((this.shared.get(sessionId) ?? 0) > 0) {
        throw new SessionArchivingError(sessionId, "shared");
      }
    }
    for (const sessionId of uniqueSessionIds) {
      this.exclusive.add(sessionId);
    }
    try {
      return await fn();
    } finally {
      for (const sessionId of uniqueSessionIds) {
        this.exclusive.delete(sessionId);
      }
    }
  }
  async runSharedMany(sessionIds, fn) {
    const uniqueSessionIds = [...new Set(sessionIds)];
    for (const sessionId of uniqueSessionIds) {
      this.assertNotTransitioning(sessionId);
    }
    for (const sessionId of uniqueSessionIds) {
      this.shared.set(sessionId, (this.shared.get(sessionId) ?? 0) + 1);
    }
    try {
      return await fn();
    } finally {
      for (const sessionId of uniqueSessionIds) {
        const count = (this.shared.get(sessionId) ?? 1) - 1;
        if (count <= 0) {
          this.shared.delete(sessionId);
        } else {
          this.shared.set(sessionId, count);
        }
      }
    }
  }
};
async function deleteDaemonSessions(params) {
  const { sessionIds, service, bridge, coordinator, onError } = params;
  const uniqueSessionIds = [...new Set(sessionIds)];
  const closeErrors = [];
  const removed = [];
  const notFound = [];
  const removeErrors = [];
  for (const sessionId of uniqueSessionIds) {
    coordinator.assertNotTransitioning(sessionId);
  }
  await Promise.all(
    uniqueSessionIds.map(async (sessionId) => {
      try {
        await coordinator.runExclusiveMany([sessionId], async () => {
          let shouldRemove = false;
          try {
            await bridge.closeSession(sessionId);
            shouldRemove = true;
          } catch (closeErr) {
            if (closeErr instanceof SessionNotFoundError || closeErr instanceof Error && closeErr.name === "SessionNotFoundError") {
              shouldRemove = true;
            } else {
              const message = closeErr instanceof Error ? closeErr.message : String(closeErr);
              onError?.({ phase: "close", sessionId, error: message });
              closeErrors.push({ sessionId, error: message });
            }
          }
          if (!shouldRemove) return;
          try {
            if (await service.removeSession(sessionId)) {
              removed.push(sessionId);
            } else {
              notFound.push(sessionId);
            }
          } catch (removeErr) {
            const message = removeErr instanceof Error ? removeErr.message : String(removeErr);
            onError?.({ phase: "remove", sessionId, error: message });
            removeErrors.push({ sessionId, error: message });
          }
        });
      } catch (err) {
        if (err instanceof SessionArchivingError && err.lockKind === "exclusive") {
          throw err;
        }
        const message = err instanceof Error ? err.message : String(err);
        onError?.({ phase: "delete", sessionId, error: message });
        closeErrors.push({ sessionId, error: message });
      }
    })
  );
  return { removed, notFound, errors: [...closeErrors, ...removeErrors] };
}
__name(deleteDaemonSessions, "deleteDaemonSessions");
async function assertSessionLoadable(workspaceCwd, sessionId) {
  const location = await new SessionService(workspaceCwd).getSessionLocation(
    sessionId
  );
  if (location === "archived") {
    throw new SessionArchivedError(sessionId);
  }
  if (location === "conflict") {
    throw new SessionConflictError(sessionId);
  }
}
__name(assertSessionLoadable, "assertSessionLoadable");
function isSessionNotFoundError(err) {
  return err instanceof SessionNotFoundError || err instanceof Error && err.name === "SessionNotFoundError";
}
__name(isSessionNotFoundError, "isSessionNotFoundError");
async function classifySessionLocations(service, sessionIds) {
  const result = {
    active: [],
    archived: [],
    notFound: [],
    errors: []
  };
  const locationResults = await Promise.allSettled(
    sessionIds.map(async (sessionId) => ({
      sessionId,
      location: await service.getSessionLocation(sessionId)
    }))
  );
  for (let i = 0; i < locationResults.length; i++) {
    const sessionId = sessionIds[i];
    const locationResult = locationResults[i];
    if (locationResult.status === "rejected") {
      result.errors.push({ sessionId, error: locationResult.reason });
      continue;
    }
    const location = locationResult.value.location;
    if (location === void 0) {
      result.notFound.push(sessionId);
    } else if (location === "archived") {
      result.archived.push(sessionId);
    } else if (location === "conflict") {
      result.errors.push({
        sessionId,
        error: new Error(`Session archive conflict: ${sessionId}`)
      });
    } else {
      result.active.push(sessionId);
    }
  }
  return result;
}
__name(classifySessionLocations, "classifySessionLocations");
function logSessionArchiveResult(action, result) {
  const changedLabel = action === "archive" ? "archived" : "unarchived";
  const alreadyLabel = action === "archive" ? "alreadyArchived" : "alreadyActive";
  const details = [
    `requested=${result.requested.length} requestedIds=${formatSessionIds(result.requested)}`,
    `${changedLabel}=${result.changed.length} ${changedLabel}Ids=${formatSessionIds(result.changed)}`,
    `${alreadyLabel}=${result.already.length} ${alreadyLabel}Ids=${formatSessionIds(result.already)}`,
    `notFound=${result.notFound.length} notFoundIds=${formatSessionIds(result.notFound)}`,
    `errors=${result.errors.length} errorIds=${formatSessionErrors(result.errors)}`
  ].join(" ");
  writeStderrLine(`qwen serve: sessions ${action} result ${details}`);
}
__name(logSessionArchiveResult, "logSessionArchiveResult");
function formatSessionIds(sessionIds) {
  return `[${sessionIds.map((sessionId) => safeLogValue(sessionId)).join(",")}]`;
}
__name(formatSessionIds, "formatSessionIds");
function formatSessionErrors(errors) {
  return `[${errors.map(
    ({ sessionId, error: error2 }) => `${safeLogValue(sessionId)}:${safeLogValue(errorMessage(error2))}`
  ).join(",")}]`;
}
__name(formatSessionErrors, "formatSessionErrors");
function errorMessage(error2) {
  return error2 instanceof Error ? error2.message : String(error2);
}
__name(errorMessage, "errorMessage");
function logSessionArchiveWarning(message) {
  writeStderrLine(`qwen serve: ${sanitizeLogLine(message)}`);
}
__name(logSessionArchiveWarning, "logSessionArchiveWarning");
var LOG_LINE_UNSAFE_RE = /[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g;
function sanitizeLogLine(message) {
  return message.replace(LOG_LINE_UNSAFE_RE, " ").slice(0, 4096);
}
__name(sanitizeLogLine, "sanitizeLogLine");
async function archiveDaemonSessions(params) {
  const { sessionIds, service, bridge, coordinator } = params;
  const uniqueSessionIds = [...new Set(sessionIds)];
  const archived = [];
  const alreadyArchived = [];
  const notFound = [];
  const errors = [];
  const initial = await classifySessionLocations(service, uniqueSessionIds);
  const activeIds = initial.active;
  alreadyArchived.push(...initial.archived);
  notFound.push(...initial.notFound);
  errors.push(...initial.errors);
  if (activeIds.length > 0) {
    await coordinator.runExclusiveMany(activeIds, async () => {
      const locked = await classifySessionLocations(service, activeIds);
      const closableIds = locked.active;
      alreadyArchived.push(...locked.archived);
      notFound.push(...locked.notFound);
      errors.push(...locked.errors);
      const closeResults = await Promise.allSettled(
        closableIds.map(async (sessionId) => {
          try {
            await bridge.closeSession(sessionId, void 0, {
              requireAgentClose: true
            });
          } catch (err) {
            if (!isSessionNotFoundError(err)) {
              throw err;
            }
          }
        })
      );
      const archiveIds = [];
      for (let i = 0; i < closeResults.length; i++) {
        const sessionId = closableIds[i];
        const result = closeResults[i];
        if (result.status === "fulfilled") {
          archiveIds.push(sessionId);
        } else {
          errors.push({ sessionId, error: result.reason });
        }
      }
      try {
        const archiveResult = await service.archiveSessions(archiveIds, {
          knownLocation: "active"
        });
        archived.push(...archiveResult.archived);
        alreadyArchived.push(...archiveResult.alreadyArchived);
        notFound.push(...archiveResult.notFound);
        errors.push(...archiveResult.errors);
      } catch (err) {
        for (const sessionId of archiveIds) {
          errors.push({ sessionId, error: err });
        }
      }
    });
  }
  logSessionArchiveResult("archive", {
    requested: uniqueSessionIds,
    changed: archived,
    already: alreadyArchived,
    notFound,
    errors
  });
  return { archived, alreadyArchived, notFound, errors };
}
__name(archiveDaemonSessions, "archiveDaemonSessions");
async function unarchiveDaemonSessions(params) {
  const { sessionIds, service, coordinator } = params;
  const uniqueSessionIds = [...new Set(sessionIds)];
  const unarchived = [];
  const alreadyActive = [];
  const notFound = [];
  const errors = [];
  const initial = await classifySessionLocations(service, uniqueSessionIds);
  const archivedIds = initial.archived;
  alreadyActive.push(...initial.active);
  notFound.push(...initial.notFound);
  errors.push(...initial.errors);
  if (archivedIds.length > 0) {
    await coordinator.runExclusiveMany(archivedIds, async () => {
      const locked = await classifySessionLocations(service, archivedIds);
      const unarchiveIds = locked.archived;
      alreadyActive.push(...locked.active);
      notFound.push(...locked.notFound);
      errors.push(...locked.errors);
      if (unarchiveIds.length > 0) {
        try {
          const result = await service.unarchiveSessions(unarchiveIds, {
            knownLocation: "archived"
          });
          unarchived.push(...result.unarchived);
          alreadyActive.push(...result.alreadyActive);
          notFound.push(...result.notFound);
          errors.push(...result.errors);
        } catch (err) {
          for (const sessionId of unarchiveIds) {
            errors.push({ sessionId, error: err });
          }
        }
      }
    });
  }
  logSessionArchiveResult("unarchive", {
    requested: uniqueSessionIds,
    changed: unarchived,
    already: alreadyActive,
    notFound,
    errors
  });
  return { unarchived, alreadyActive, notFound, errors };
}
__name(unarchiveDaemonSessions, "unarchiveDaemonSessions");

// packages/cli/src/serve/acp-http/json-rpc.ts
init_esbuild_shims();
var QWEN_METHOD_NS = "_qwen/";
var QWEN_META_KEY = "qwen";
var RPC = {
  PARSE_ERROR: -32700,
  INVALID_REQUEST: -32600,
  METHOD_NOT_FOUND: -32601,
  INVALID_PARAMS: -32602,
  INTERNAL_ERROR: -32603
};
function isObject(v) {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}
__name(isObject, "isObject");
function isRequest(m) {
  return isObject(m) && m["jsonrpc"] === "2.0" && typeof m["method"] === "string" && "id" in m && m["id"] !== null && (typeof m["id"] === "number" || typeof m["id"] === "string");
}
__name(isRequest, "isRequest");
function isNotification(m) {
  return isObject(m) && m["jsonrpc"] === "2.0" && typeof m["method"] === "string" && !("id" in m);
}
__name(isNotification, "isNotification");
function isResponse(m) {
  return isObject(m) && m["jsonrpc"] === "2.0" && !("method" in m) && "id" in m && // JSON-RPC 2.0 §5: EXACTLY one of result/error (XOR). Accepting both
  // would let a buggy client's approval (result + error) be misread as a
  // cancellation by the `'error' in msg` check downstream. A dual-field
  // message therefore fails isRequest/isNotification/isResponse →
  // `parseInbound` rejects it → the POST handler returns 400 (logged by the
  // malformed-request path in index.ts), so the client is told its vote was
  // not accepted (not a silent drop); it can retry with a valid response,
  // and teardown still releases the pending entry if it doesn't.
  "result" in m !== "error" in m;
}
__name(isResponse, "isResponse");
var LOG_SAFE_RE = new RegExp(
  String.raw`[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]`,
  "g"
);
function logSafe(s) {
  return s.replace(LOG_SAFE_RE, " ");
}
__name(logSafe, "logSafe");
function success(id, result) {
  return { jsonrpc: "2.0", id, result };
}
__name(success, "success");
function error(id, code, message, data) {
  return {
    jsonrpc: "2.0",
    id,
    error: { code, message, ...data !== void 0 ? { data } : {} }
  };
}
__name(error, "error");
function notification(method, params) {
  return { jsonrpc: "2.0", method, params };
}
__name(notification, "notification");
function request(id, method, params) {
  return { jsonrpc: "2.0", id, method, params };
}
__name(request, "request");
function parseInbound(raw) {
  if (Array.isArray(raw)) {
    return {
      ok: false,
      error: error(null, RPC.INVALID_REQUEST, "JSON-RPC batch not supported")
    };
  }
  if (isRequest(raw) || isNotification(raw) || isResponse(raw)) {
    return { ok: true, message: raw };
  }
  return {
    ok: false,
    error: error(null, RPC.INVALID_REQUEST, "Malformed JSON-RPC message")
  };
}
__name(parseInbound, "parseInbound");

// packages/cli/src/serve/acp-http/dispatch.ts
function errMsg(err) {
  return err instanceof Error ? err.message : String(err);
}
__name(errMsg, "errMsg");
var SESSION_SHELL_METHOD = `${QWEN_METHOD_NS}session/shell`;
var INVALID_PERMISSION_OUTCOME_ERROR = '`outcome` must be `{ outcome: "cancelled" }` or `{ outcome: "selected", optionId: string }`';
var ALL_QWEN_VENDOR_METHODS = [
  `${QWEN_METHOD_NS}session/heartbeat`,
  `${QWEN_METHOD_NS}session/context`,
  `${QWEN_METHOD_NS}session/supported_commands`,
  `${QWEN_METHOD_NS}session/update_metadata`,
  `${QWEN_METHOD_NS}workspace/mcp`,
  `${QWEN_METHOD_NS}workspace/skills`,
  `${QWEN_METHOD_NS}workspace/providers`,
  `${QWEN_METHOD_NS}workspace/env`,
  `${QWEN_METHOD_NS}workspace/preflight`,
  `${QWEN_METHOD_NS}workspace/init`,
  `${QWEN_METHOD_NS}workspace/trust`,
  `${QWEN_METHOD_NS}workspace/trust/request`,
  `${QWEN_METHOD_NS}workspace/permissions`,
  `${QWEN_METHOD_NS}workspace/permissions/set`,
  `${QWEN_METHOD_NS}workspace/voice`,
  `${QWEN_METHOD_NS}workspace/voice/set`,
  `${QWEN_METHOD_NS}workspace/setup-github`,
  `${QWEN_METHOD_NS}workspace/set_tool_enabled`,
  `${QWEN_METHOD_NS}workspace/restart_mcp_server`,
  // Wave 1: session extensions
  `${QWEN_METHOD_NS}session/recap`,
  `${QWEN_METHOD_NS}session/btw`,
  SESSION_SHELL_METHOD,
  `${QWEN_METHOD_NS}session/detach`,
  `${QWEN_METHOD_NS}session/context_usage`,
  `${QWEN_METHOD_NS}session/tasks`,
  `${QWEN_METHOD_NS}session/lsp`,
  // Wave 1: memory
  `${QWEN_METHOD_NS}workspace/memory`,
  `${QWEN_METHOD_NS}workspace/memory/write`,
  `${QWEN_METHOD_NS}workspace/memory/remember`,
  `${QWEN_METHOD_NS}workspace/memory/remember/get`,
  // Wave 1: files
  `${QWEN_METHOD_NS}file/read`,
  `${QWEN_METHOD_NS}file/read_bytes`,
  `${QWEN_METHOD_NS}file/stat`,
  `${QWEN_METHOD_NS}file/list`,
  `${QWEN_METHOD_NS}file/glob`,
  `${QWEN_METHOD_NS}file/write`,
  `${QWEN_METHOD_NS}file/edit`,
  // Wave 1: auth
  `${QWEN_METHOD_NS}workspace/auth/status`,
  `${QWEN_METHOD_NS}workspace/auth/device_flow/start`,
  `${QWEN_METHOD_NS}workspace/auth/device_flow/get`,
  `${QWEN_METHOD_NS}workspace/auth/device_flow/cancel`,
  // Wave 1: remaining workspace
  `${QWEN_METHOD_NS}workspace/tools`,
  `${QWEN_METHOD_NS}workspace/mcp/tools`,
  `${QWEN_METHOD_NS}workspace/mcp/resources`,
  `${QWEN_METHOD_NS}workspace/mcp/servers/add`,
  `${QWEN_METHOD_NS}workspace/mcp/servers/remove`,
  `${QWEN_METHOD_NS}sessions/delete`,
  `${QWEN_METHOD_NS}sessions/archive`,
  `${QWEN_METHOD_NS}sessions/unarchive`,
  // Wave 2: agents
  `${QWEN_METHOD_NS}workspace/agents/list`,
  `${QWEN_METHOD_NS}workspace/agents/get`,
  `${QWEN_METHOD_NS}workspace/agents/create`,
  `${QWEN_METHOD_NS}workspace/agents/update`,
  `${QWEN_METHOD_NS}workspace/agents/delete`
];
function advertisedQwenVendorMethods(sessionShellCommandEnabled) {
  return ALL_QWEN_VENDOR_METHODS.filter(
    (method) => sessionShellCommandEnabled || method !== SESSION_SHELL_METHOD
  );
}
__name(advertisedQwenVendorMethods, "advertisedQwenVendorMethods");
var CONN_ROUTED_METHODS = /* @__PURE__ */ new Set([
  "authenticate",
  "session/new",
  "session/load",
  "session/resume",
  "session/list",
  "session/close",
  "session/fork",
  "session/permission",
  ...ALL_QWEN_VENDOR_METHODS
]);
var MAX_NAME_LENGTH = 256;
var DEFAULT_FILE_GLOB_MAX_RESULTS = 5e3;
var MAX_FILE_GLOB_MAX_RESULTS = 5e4;
var MAX_FILE_LINE_LIMIT2 = 2e3;
var AcpParamError = class extends Error {
  static {
    __name(this, "AcpParamError");
  }
};
function parseOptionalPositiveInteger(value, fallback, max) {
  if (value === void 0) return fallback;
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > max) {
    return null;
  }
  return value;
}
__name(parseOptionalPositiveInteger, "parseOptionalPositiveInteger");
function parseOptionalSafeIntegerInRange(value, min, max) {
  if (value === void 0) return void 0;
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) {
    return null;
  }
  return value;
}
__name(parseOptionalSafeIntegerInRange, "parseOptionalSafeIntegerInRange");
function parseOptionalWorkspaceCwd2(params, boundWorkspace) {
  if (!("cwd" in params) || params["cwd"] === void 0) return boundWorkspace;
  const cwd = params["cwd"];
  if (typeof cwd !== "string") {
    throw new AcpParamError(
      "`cwd` must be a string absolute path when provided"
    );
  }
  if (cwd.length > MAX_WORKSPACE_PATH_LENGTH) {
    throw new AcpParamError(
      `\`cwd\` exceeds the ${MAX_WORKSPACE_PATH_LENGTH}-character limit`
    );
  }
  if (!path8.isAbsolute(cwd)) {
    throw new AcpParamError("`cwd` must be an absolute path when provided");
  }
  return cwd;
}
__name(parseOptionalWorkspaceCwd2, "parseOptionalWorkspaceCwd");
function validatePrompt(params) {
  const prompt = params["prompt"];
  if (!Array.isArray(prompt) || prompt.length === 0) {
    throw new AcpParamError(
      "`prompt` is required and must be a non-empty array of content blocks"
    );
  }
  if (!prompt.every(
    (b) => typeof b === "object" && b !== null && !Array.isArray(b)
  )) {
    throw new AcpParamError("each `prompt` element must be an object");
  }
}
__name(validatePrompt, "validatePrompt");
function parsePermissionResponse(params) {
  const outcome = params["outcome"];
  if (!isObject(outcome)) {
    throw new AcpParamError(INVALID_PERMISSION_OUTCOME_ERROR);
  }
  if (outcome["outcome"] !== "cancelled") {
    if (outcome["outcome"] !== "selected" || typeof outcome["optionId"] !== "string" || outcome["optionId"].length === 0) {
      throw new AcpParamError(INVALID_PERMISSION_OUTCOME_ERROR);
    }
  }
  const cleanOutcome = outcome["outcome"] === "cancelled" ? { outcome: "cancelled" } : { outcome: "selected", optionId: outcome["optionId"] };
  const response = { outcome: cleanOutcome };
  const answers = params["answers"];
  if (answers !== void 0) {
    if (isObject(answers) && Object.values(answers).every((value) => typeof value === "string")) {
      response["answers"] = answers;
    } else {
      writeStderrLine(
        "qwen serve: /acp session/permission dropping invalid `answers` (expected an object map of string values)"
      );
    }
  }
  if (isObject(params["_meta"])) {
    response["_meta"] = params["_meta"];
  }
  return response;
}
__name(parsePermissionResponse, "parsePermissionResponse");
function toRpcError(err) {
  if (err instanceof AcpParamError || err instanceof InvalidCursorError) {
    return { code: RPC.INVALID_PARAMS, message: err.message };
  }
  if (err instanceof SubagentError) {
    return { code: RPC.INVALID_PARAMS, message: err.message };
  }
  if (err instanceof FsError) {
    return {
      code: RPC.INVALID_PARAMS,
      message: err.message,
      data: { errorKind: err.kind, hint: err.hint }
    };
  }
  if (err instanceof WorkspaceMemoryFileTooLargeError) {
    return {
      code: RPC.INVALID_PARAMS,
      message: err.message,
      data: { errorKind: "memory_file_too_large" }
    };
  }
  if (err instanceof WorkspaceMemoryWriteTimeoutError) {
    return {
      code: RPC.INTERNAL_ERROR,
      message: err.message,
      data: { errorKind: "memory_write_timeout" }
    };
  }
  if (err instanceof WorkspaceVoiceError) {
    return {
      code: err.status >= 400 && err.status < 500 ? RPC.INVALID_PARAMS : RPC.INTERNAL_ERROR,
      message: err.message,
      data: { errorKind: err.code }
    };
  }
  if (err instanceof WorkspaceSettingsPartialPersistError) {
    return {
      code: RPC.INTERNAL_ERROR,
      message: err.message,
      data: {
        errorKind: "partial_persist_error",
        committedKeys: err.committedWrites.map((write) => write.key)
      }
    };
  }
  if (err instanceof TooManyActiveDeviceFlowsError) {
    return {
      code: RPC.INTERNAL_ERROR,
      message: err.message,
      data: { errorKind: "too_many_active_flows" }
    };
  }
  if (err instanceof UnsupportedDeviceFlowProviderError) {
    return {
      code: RPC.INVALID_PARAMS,
      message: err.message,
      data: { errorKind: "unsupported_provider" }
    };
  }
  if (err instanceof UpstreamDeviceFlowError) {
    return {
      code: RPC.INTERNAL_ERROR,
      message: err.message,
      data: { errorKind: "upstream_error" }
    };
  }
  if (err instanceof SessionShellDisabledError) {
    return {
      code: RPC.INVALID_PARAMS,
      message: errMsg(err),
      data: { errorKind: "session_shell_disabled" }
    };
  }
  if (err instanceof SessionShellClientRequiredError) {
    return {
      code: RPC.INVALID_PARAMS,
      message: errMsg(err),
      data: { errorKind: "client_id_required" }
    };
  }
  const name = err instanceof Error ? err.name : "";
  switch (name) {
    case "SessionArchivedError":
      return {
        code: RPC.INTERNAL_ERROR,
        message: errMsg(err),
        data: {
          errorKind: "session_archived",
          sessionId: err.sessionId
        }
      };
    case "SessionConflictError":
      return {
        code: RPC.INTERNAL_ERROR,
        message: errMsg(err),
        data: {
          errorKind: "session_conflict",
          sessionId: err.sessionId
        }
      };
    case "SessionArchivingError":
      return {
        code: RPC.INTERNAL_ERROR,
        message: errMsg(err),
        data: {
          errorKind: "session_archiving",
          sessionId: err.sessionId
        }
      };
    case "SessionNotFoundError":
    case "InvalidSessionScopeError":
    case "WorkspaceMismatchError":
    case "InvalidClientIdError":
      return { code: RPC.INVALID_PARAMS, message: errMsg(err) };
    case "SessionLimitExceededError":
      return { code: RPC.INTERNAL_ERROR, message: errMsg(err) };
    case "PromptQueueFullError": {
      const promptErr = err;
      return {
        code: RPC.INTERNAL_ERROR,
        message: errMsg(err),
        data: {
          errorKind: "prompt_queue_full",
          sessionId: promptErr.sessionId,
          limit: promptErr.limit,
          pendingCount: promptErr.pendingCount
        }
      };
    }
    default:
      return {
        code: RPC.INTERNAL_ERROR,
        message: "Internal error",
        data: { errorKind: "internal" }
      };
  }
}
__name(toRpcError, "toRpcError");
function rpcErrorFrame(id, err) {
  const { code, message, data } = toRpcError(err);
  return error(id, code, message, data);
}
__name(rpcErrorFrame, "rpcErrorFrame");
var ACP_PROTOCOL_VERSION = 1;
var AcpDispatcher = class {
  constructor(bridge, boundWorkspace, workspace, workspaceRememberLane, fsFactory, deviceFlowRegistry, sessionShellCommandEnabled = false, registry, archiveCoordinator = new SessionArchiveCoordinator()) {
    this.bridge = bridge;
    this.boundWorkspace = boundWorkspace;
    this.workspace = workspace;
    this.workspaceRememberLane = workspaceRememberLane;
    this.fsFactory = fsFactory;
    this.deviceFlowRegistry = deviceFlowRegistry;
    this.sessionShellCommandEnabled = sessionShellCommandEnabled;
    this.registry = registry;
    this.archiveCoordinator = archiveCoordinator;
    this.agentManager = createDaemonSubagentManager(boundWorkspace);
  }
  static {
    __name(this, "AcpDispatcher");
  }
  agentManager;
  killOrphanSession(sessionId) {
    void this.bridge.killSession(sessionId, { requireZeroAttaches: true }).catch(
      (err) => writeStderrLine(
        `qwen serve: /acp orphan killSession(${logSafe(sessionId)}) failed: ${logSafe(errMsg(err))}`
      )
    );
  }
  /**
   * Build the `WorkspaceRequestContext` for workspace-scoped operations
   * routed through the workspace service. The ACP dispatch has no session
   * context, so `sessionId` is omitted.
   */
  wsCtx(conn, method) {
    return {
      originatorClientId: conn.clientId,
      route: `ACP ${method}`,
      workspaceCwd: this.boundWorkspace
    };
  }
  parseSessionIds(params) {
    const sessionIds = params["sessionIds"];
    if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((s) => typeof s === "string")) {
      throw new AcpParamError(
        "`sessionIds` must be non-empty string array (max 100)"
      );
    }
    return [...new Set(sessionIds)];
  }
  serializeSessionErrors(errors) {
    return errors.map((e) => ({
      sessionId: e.sessionId,
      error: errMsg(e.error)
    }));
  }
  /**
   * Build the bridge context for a per-session call. Echoes the clientId the
   * bridge STAMPED at create/attach (the connection's own id is unregistered
   * and would be rejected) and threads `fromLoopback` so the `local-only`
   * permission policy can gate votes by transport — symmetric with the REST
   * surface's `detectFromLoopback(req)`.
   *
   * Throws when no stamped clientId is present: the only callers reach here
   * AFTER `requireOwned`, so the binding must exist and carry the bridge's
   * id. A missing id means an invariant broke (a `session/new`/`load` that
   * didn't record it) — fail loud rather than silently send an unregistered
   * id whose rejection surfaces asynchronously, far from the cause.
   */
  sessionCtx(conn, sessionId, fromLoopback) {
    const clientId = conn.sessions.get(sessionId)?.clientId;
    if (!clientId) {
      throw new Error(
        `no bridge-stamped clientId for session ${sessionId} (ownership invariant violated)`
      );
    }
    return { clientId, fromLoopback };
  }
  /**
   * The session's ACP-shaped config options (model/mode/…), read from the
   * child's own session state. Returned in `session/new` and as the result
   * of `session/set_config_option`. Best-effort — `undefined` on error.
   */
  async configOptionsFor(sessionId) {
    try {
      const ctx = await this.bridge.getSessionContextStatus(sessionId);
      const co = ctx?.state?.configOptions;
      return Array.isArray(co) ? co : void 0;
    } catch (err) {
      writeStderrLine(
        `qwen serve: /acp configOptionsFor(${logSafe(sessionId)}) failed: ${logSafe(errMsg(err))}`
      );
      return void 0;
    }
  }
  /**
   * Extract ACP-standard `SessionModelState` from configOptions.
   * ConfigOptions carry model info as `{ category: 'model', type: 'select',
   * currentValue, options }`. Maps to `{ currentModelId, availableModels }`.
   */
  extractModelState(configOptions) {
    if (!configOptions) return void 0;
    const modelOpt = configOptions.find(
      (o) => typeof o === "object" && o !== null && o["category"] === "model"
    );
    if (!modelOpt) return void 0;
    const currentModelId = String(modelOpt["currentValue"] ?? "");
    const options = Array.isArray(modelOpt["options"]) ? modelOpt["options"] : [];
    return {
      currentModelId,
      availableModels: options.map((opt) => {
        const o = opt;
        return { id: String(o["value"] ?? o["id"] ?? "") };
      })
    };
  }
  /**
   * Extract ACP-standard `SessionModeState` from configOptions.
   * ConfigOptions carry mode info as `{ category: 'mode', type: 'select',
   * currentValue, options }`. Maps to `{ currentModeId, availableModes }`.
   */
  extractModeState(configOptions) {
    if (!configOptions) return void 0;
    const modeOpt = configOptions.find(
      (o) => typeof o === "object" && o !== null && o["category"] === "mode"
    );
    if (!modeOpt) return void 0;
    const currentModeId = String(modeOpt["currentValue"] ?? "");
    const options = Array.isArray(modeOpt["options"]) ? modeOpt["options"] : [];
    return {
      currentModeId,
      availableModes: options.map((opt) => {
        const o = opt;
        return { id: String(o["value"] ?? o["id"] ?? "") };
      })
    };
  }
  /**
   * Cancel a permission request the client abandoned (closed its stream /
   * connection before voting), so the bridge isn't left blocked. Invoked
   * by the connection-registry teardown path.
   */
  cancelAbandonedPermission(req, clientId) {
    try {
      this.bridge.respondToSessionPermission(
        req.sessionId,
        req.bridgeRequestId,
        { outcome: { outcome: "cancelled" } },
        clientId !== void 0 ? { clientId } : void 0
      );
      return true;
    } catch (err) {
      const msg = errMsg(err);
      if (/not found|unknown session/i.test(msg)) return true;
      writeStderrLine(
        `qwen serve: /acp cancelAbandonedPermission(${logSafe(req.sessionId)}) failed: ${logSafe(msg)}`
      );
      return false;
    }
  }
  /**
   * Build the `initialize` result advertising standard + `_qwen` caps.
   * Negotiates the protocol version: we only implement stable V1, so we
   * clamp to `[1, ACP_PROTOCOL_VERSION]` — a client asking for 0/negative
   * (ACP marks V0 a pre-release fallback) or a future version gets `1`
   * rather than an echoed version we don't actually implement.
   */
  buildInitializeResult(connectionId, requestedVersion) {
    const requested = typeof requestedVersion === "number" && Number.isFinite(requestedVersion) ? requestedVersion : ACP_PROTOCOL_VERSION;
    const negotiated = Math.max(1, Math.min(requested, ACP_PROTOCOL_VERSION));
    return {
      protocolVersion: negotiated,
      agentCapabilities: {
        loadSession: true,
        // Mirror acpAgent.ts promptCapabilities: #resolvePrompt handles audio
        // blocks identically to image (both become inlineData Parts).
        promptCapabilities: {
          image: true,
          audio: true,
          embeddedContext: true
        },
        // Model + mode are exposed via the STANDARD `session/set_config_option`
        // (categories `model`/`mode`); advertise that here.
        configOptions: true,
        // Vendor extensions are advertised under `_meta` keyed by domain
        // (ACP convention, e.g. `_meta: { "zed.dev": … }`). Clients
        // feature-detect before calling `_qwen/…` methods.
        _meta: {
          [QWEN_META_KEY]: {
            connectionId,
            workspaceCwd: this.boundWorkspace,
            methods: advertisedQwenVendorMethods(
              this.sessionShellCommandEnabled
            )
          }
        }
      }
    };
  }
  /**
   * Gate a per-session operation on connection ownership. Sends a JSON-RPC
   * error and returns false when this connection never created/attached
   * the session (prevents driving or eavesdropping on another
   * connection's session). `session/new|load|resume` are the
   * ownership-GRANTING ops and skip this.
   */
  requireOwned(conn, sessionId, id) {
    if (conn.ownsSession(sessionId)) return true;
    if (id === void 0) {
      writeStderrLine(
        `qwen serve: /acp notification for unowned session ${logSafe(sessionId)} (dropped)`
      );
      return false;
    }
    conn.sendConn(
      error(
        id,
        RPC.INVALID_PARAMS,
        `Session ${sessionId} is not owned by this connection`
      )
    );
    return false;
  }
  async withMutableOwned(conn, sessionId, id, fn) {
    if (!this.requireOwned(conn, sessionId, id)) return;
    await this.archiveCoordinator.runSharedMany([sessionId], async () => {
      await fn();
    });
  }
  findPendingClientRequest(conn, id) {
    const req = conn.pending.get(id);
    if (req) return { conn, id, req };
    return this.registry?.findPendingClientRequest(id);
  }
  dropResolvedPermission(conn, id) {
    conn.pending.delete(id);
  }
  /**
   * Drop ONLY the calling connection's own pending permission entry for
   * `requestId`, never a sibling co-owner's. Under the consensus policy a vote
   * (or an unexpected vote error) from connection B must not delete connection
   * A's still-needed entry, which would stall the quorum. A connection that
   * never streamed the request holds no entry, so this is a no-op for it.
   */
  dropOwnPendingPermission(conn, requestId) {
    for (const [pid, preq] of conn.pending) {
      if (preq.kind === "permission" && preq.bridgeRequestId === requestId) {
        conn.pending.delete(pid);
        return;
      }
    }
  }
  /**
   * Handle one inbound POST message. Returns nothing — every reply is
   * delivered asynchronously on a long-lived SSE stream per the RFD
   * (`POST` itself answers `202`). `initialize` is handled by the caller
   * (it mints the connection) and never reaches here.
   */
  async handle(conn, msg, sessionHeader, reqLoopback) {
    const loopback = reqLoopback ?? conn.fromLoopback;
    if (isResponse(msg)) {
      try {
        this.resolveClientResponse(conn, msg, loopback);
      } catch (err) {
        writeStderrLine(
          `qwen serve: /acp response handling error: ${logSafe(errMsg(err))}`
        );
      }
      return;
    }
    if (!isRequest(msg) && !isNotification(msg)) return;
    const method = msg.method;
    const params = isObject(msg.params) ? msg.params : {};
    const id = isRequest(msg) ? msg.id : void 0;
    if (sessionHeader && typeof params["sessionId"] === "string" && params["sessionId"] !== sessionHeader) {
      if (id !== void 0) {
        conn.sendConn(
          error(
            id,
            RPC.INVALID_PARAMS,
            "Acp-Session-Id header does not match params.sessionId"
          )
        );
      }
      return;
    }
    try {
      switch (method) {
        case "authenticate":
          this.replyConn(conn, id, {});
          return;
        case "session/new": {
          const cwd = parseOptionalWorkspaceCwd2(params, this.boundWorkspace);
          const session = await this.bridge.spawnOrAttach({
            workspaceCwd: cwd,
            clientId: conn.clientId,
            sessionScope: "thread"
          });
          if (conn.destroyed) {
            this.killOrphanSession(session.sessionId);
            return;
          }
          conn.getOrCreateSession(session.sessionId).clientId = session.clientId;
          conn.ownSession(session.sessionId);
          const configOptions = await this.configOptionsFor(session.sessionId);
          if (conn.destroyed) {
            this.killOrphanSession(session.sessionId);
            return;
          }
          const models = this.extractModelState(configOptions);
          const modes = this.extractModeState(configOptions);
          this.replyConn(conn, id, {
            sessionId: session.sessionId,
            ...configOptions ? { configOptions } : {},
            ...models ? { models } : {},
            ...modes ? { modes } : {}
          });
          return;
        }
        case "session/load":
        case "session/resume": {
          const sessionId = String(params["sessionId"] ?? "");
          if (!sessionId) {
            if (id !== void 0) {
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`sessionId` is required")
              );
            }
            return;
          }
          if (conn.closingSessions.has(sessionId)) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INTERNAL_ERROR,
                  `session ${sessionId} is being closed; retry`
                )
              );
            }
            return;
          }
          const cwd = parseOptionalWorkspaceCwd2(params, this.boundWorkspace);
          const restored = await this.archiveCoordinator.runSharedMany(
            [sessionId],
            async () => {
              await assertSessionLoadable(cwd, sessionId);
              return method === "session/load" ? await this.bridge.loadSession({
                sessionId,
                workspaceCwd: cwd,
                clientId: conn.clientId
              }) : await this.bridge.resumeSession({
                sessionId,
                workspaceCwd: cwd,
                clientId: conn.clientId
              });
            }
          );
          const closeRaced = conn.closingSessions.has(sessionId);
          if (conn.destroyed || closeRaced) {
            const cleanup = restored.attached ? this.bridge.detachClient(sessionId, restored.clientId) : this.bridge.killSession(sessionId, {
              requireZeroAttaches: true
            });
            void cleanup.catch(
              (err) => writeStderrLine(
                `qwen serve: /acp orphan ${restored.attached ? "detach" : "kill"}(${logSafe(sessionId)}) teardown-race: ${logSafe(errMsg(err))}`
              )
            );
            if (closeRaced && !conn.destroyed && id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INTERNAL_ERROR,
                  `session ${sessionId} was closed during load; retry`
                )
              );
            }
            return;
          }
          conn.getOrCreateSession(sessionId).clientId = restored.clientId;
          conn.ownSession(sessionId);
          const loadConfigOptions = await this.configOptionsFor(sessionId);
          const loadModels = this.extractModelState(loadConfigOptions);
          const loadModes = this.extractModeState(loadConfigOptions);
          this.replyConn(conn, id, {
            ...restored.state ?? {},
            ...loadConfigOptions ? { configOptions: loadConfigOptions } : {},
            ...loadModels ? { models: loadModels } : {},
            ...loadModes ? { modes: loadModes } : {}
          });
          return;
        }
        case "session/list": {
          const rawWorkspace = typeof params["workspaceCwd"] === "string" ? params["workspaceCwd"] : void 0;
          let workspaceCwd = rawWorkspace === void 0 ? this.boundWorkspace : parseOptionalWorkspaceCwd2(
            { cwd: rawWorkspace },
            this.boundWorkspace
          );
          if (rawWorkspace !== void 0) {
            const requestedWorkspace = canonicalizeWorkspace(workspaceCwd);
            if (requestedWorkspace !== this.boundWorkspace) {
              throw new WorkspaceMismatchError(
                this.boundWorkspace,
                requestedWorkspace
              );
            }
            workspaceCwd = requestedWorkspace;
          }
          const cursor = typeof params["cursor"] === "string" ? params["cursor"] : void 0;
          const meta = isObject(params["_meta"]) ? params["_meta"] : void 0;
          const metaSize = typeof meta?.["size"] === "number" ? meta["size"] : void 0;
          const rawArchiveState = typeof params["archiveState"] === "string" ? params["archiveState"] : typeof meta?.["archiveState"] === "string" ? meta["archiveState"] : void 0;
          let archiveState;
          if (rawArchiveState !== void 0) {
            if (rawArchiveState !== "active" && rawArchiveState !== "archived") {
              throw new AcpParamError(
                '`archiveState` must be "active" or "archived"'
              );
            }
            archiveState = rawArchiveState;
          }
          const result = await listWorkspaceSessionsForResponse(
            this.bridge,
            workspaceCwd,
            { cursor, size: metaSize, archiveState }
          );
          this.replyConn(conn, id, {
            sessions: result.sessions.map((s) => ({
              sessionId: s.sessionId,
              workspaceCwd: s.workspaceCwd,
              cwd: s.workspaceCwd,
              createdAt: s.createdAt,
              updatedAt: s.updatedAt,
              displayName: s.displayName,
              title: s.displayName,
              clientCount: s.clientCount,
              hasActivePrompt: s.hasActivePrompt,
              isArchived: s.isArchived === true
            })),
            ...result.nextCursor != null ? { nextCursor: result.nextCursor } : {}
          });
          return;
        }
        case "session/close": {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.requireOwned(conn, sessionId, id)) return;
          conn.ownedSessions.delete(sessionId);
          conn.closingSessions.add(sessionId);
          let closeStarted = false;
          const closeSession = /* @__PURE__ */ __name(async () => {
            closeStarted = true;
            try {
              await this.bridge.closeSession(
                sessionId,
                this.sessionCtx(conn, sessionId, loopback)
              );
            } finally {
              try {
                conn.closeSessionStream(sessionId);
              } catch (teardownErr) {
                writeStderrLine(
                  `qwen serve: /acp session/close local teardown failed (${logSafe(sessionId)}): ${logSafe(errMsg(teardownErr))}`
                );
              }
            }
          }, "closeSession");
          try {
            try {
              await this.archiveCoordinator.runExclusiveMany(
                [sessionId],
                closeSession
              );
            } catch (err) {
              const promptAbort = conn.sessions.get(sessionId)?.promptAbort;
              if (err instanceof SessionArchivingError && err.lockKind === "shared" && promptAbort !== void 0) {
                await this.archiveCoordinator.runSharedMany(
                  [sessionId],
                  closeSession
                );
              } else {
                throw err;
              }
            }
          } catch (err) {
            if (!closeStarted) {
              conn.ownedSessions.add(sessionId);
            }
            throw err;
          } finally {
            conn.closingSessions.delete(sessionId);
          }
          this.replyConn(conn, id, {});
          return;
        }
        // ACP standard: session/fork — create a branched copy of an existing
        // session. Maps to bridge.branchSession().
        case "session/fork": {
          const sessionId = String(params["sessionId"] ?? "");
          if (!sessionId) {
            if (id !== void 0) {
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`sessionId` is required")
              );
            }
            return;
          }
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const ctx = this.sessionCtx(conn, sessionId, loopback);
            const result = await this.bridge.branchSession(
              sessionId,
              {
                name: typeof params["name"] === "string" ? params["name"] : void 0
              },
              ctx
            );
            if (conn.destroyed) {
              this.killOrphanSession(result.sessionId);
              return;
            }
            conn.getOrCreateSession(result.sessionId).clientId = result.clientId;
            conn.ownSession(result.sessionId);
            const configOptions = await this.configOptionsFor(result.sessionId);
            const models = this.extractModelState(configOptions);
            const modes = this.extractModeState(configOptions);
            this.replyConn(conn, id, {
              sessionId: result.sessionId,
              ...configOptions ? { configOptions } : {},
              ...models ? { models } : {},
              ...modes ? { modes } : {}
            });
          });
          return;
        }
        case "session/cancel": {
          const sessionId = String(params["sessionId"] ?? "");
          await this.withMutableOwned(conn, sessionId, id, async () => {
            conn.sessions.get(sessionId)?.promptAbort?.abort();
            await this.bridge.cancelSession(
              sessionId,
              // Forward client-supplied cancel fields (reason/context) while
              // force-stamping sessionId — mirrors the REST surface.
              { ...params, sessionId },
              this.sessionCtx(conn, sessionId, loopback)
            );
            if (id !== void 0) this.replySession(conn, sessionId, id, {});
          });
          return;
        }
        case "session/prompt": {
          const sessionId = String(params["sessionId"] ?? "");
          await this.withMutableOwned(conn, sessionId, id, async () => {
            validatePrompt(params);
            await this.handlePrompt(conn, sessionId, id, params, loopback);
          });
          return;
        }
        case "session/permission": {
          const requestId = typeof params["requestId"] === "string" ? params["requestId"] : "";
          if (!requestId) {
            writeStderrLine(
              `qwen serve: /acp session/permission rejected: \`requestId\` is required`
            );
            if (id !== void 0) {
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`requestId` is required", {
                  httpStatus: 400,
                  requestId
                })
              );
            }
            return;
          }
          let response;
          try {
            response = parsePermissionResponse(params);
          } catch (err) {
            if (err instanceof AcpParamError) {
              writeStderrLine(
                `qwen serve: /acp session/permission invalid params (requestId ${logSafe(requestId)}): ${logSafe(err.message)}`
              );
              if (id !== void 0) {
                conn.sendConn(
                  error(id, RPC.INVALID_PARAMS, err.message, {
                    httpStatus: 400,
                    requestId
                  })
                );
              }
              return;
            }
            throw err;
          }
          const sessionIdParam = typeof params["sessionId"] === "string" && params["sessionId"].length > 0 ? params["sessionId"] : void 0;
          const pendingRef = this.registry?.findPendingPermission(requestId);
          if (sessionIdParam && pendingRef && sessionIdParam !== pendingRef.req.sessionId) {
            writeStderrLine(
              `qwen serve: /acp session/permission vote rejected: requestId ${logSafe(requestId)} does not belong to session ${logSafe(sessionIdParam)}`
            );
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "requestId does not belong to the specified session",
                  { httpStatus: 409, sessionId: sessionIdParam, requestId }
                )
              );
            }
            return;
          }
          if (this.registry && !pendingRef) {
            writeStderrLine(
              `qwen serve: /acp session/permission vote dropped: no pending request for requestId ${logSafe(requestId)}`
            );
            if (id !== void 0) {
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "No pending permission request", {
                  httpStatus: 404,
                  requestId
                })
              );
            }
            return;
          }
          const sessionId = pendingRef?.req.sessionId ?? sessionIdParam;
          if (!sessionId) {
            writeStderrLine(
              `qwen serve: /acp session/permission vote dropped: no pending request for requestId ${logSafe(requestId)}`
            );
            if (id !== void 0) {
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "No pending permission request", {
                  httpStatus: 404,
                  requestId
                })
              );
            }
            return;
          }
          if (!conn.ownsSession(sessionId)) {
            writeStderrLine(
              `qwen serve: /acp session/permission vote rejected: session ${logSafe(sessionId)} not owned by this connection (requestId ${logSafe(requestId)})`
            );
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "Session not owned by this connection",
                  { httpStatus: 403, sessionId, requestId }
                )
              );
            }
            return;
          }
          let accepted;
          try {
            accepted = this.bridge.respondToSessionPermission(
              sessionId,
              requestId,
              response,
              this.sessionCtx(conn, sessionId, loopback)
            );
          } catch (err) {
            if (err instanceof InvalidPermissionOptionError) {
              writeStderrLine(
                `qwen serve: /acp session/permission invalid option (${logSafe(sessionId)}, requestId ${logSafe(requestId)}): ${logSafe(err.message)}`
              );
              if (id !== void 0) {
                conn.sendConn(
                  error(id, RPC.INVALID_PARAMS, err.message, {
                    httpStatus: 400,
                    code: "invalid_option_id",
                    requestId: err.requestId,
                    optionId: err.optionId
                  })
                );
              }
              return;
            }
            if (err instanceof PermissionForbiddenError) {
              writeStderrLine(
                `qwen serve: /acp session/permission forbidden (${logSafe(sessionId)}, requestId ${logSafe(requestId)}): ${logSafe(err.message)}`
              );
              if (id !== void 0) {
                conn.sendConn(
                  error(id, RPC.INVALID_PARAMS, err.message, {
                    httpStatus: 403,
                    code: "permission_forbidden",
                    requestId: err.requestId,
                    sessionId: err.sessionId,
                    reason: err.reason
                  })
                );
              }
              return;
            }
            if (err instanceof PermissionPolicyNotImplementedError) {
              writeStderrLine(
                `qwen serve: /acp session/permission policy not implemented (${logSafe(sessionId)}, requestId ${logSafe(requestId)}): ${logSafe(err.message)}`
              );
              if (id !== void 0) {
                conn.sendConn(
                  error(id, RPC.INTERNAL_ERROR, err.message, {
                    httpStatus: 501,
                    code: "permission_policy_not_implemented",
                    policy: err.policy
                  })
                );
              }
              return;
            }
            if (err instanceof CancelSentinelCollisionError) {
              writeStderrLine(
                `qwen serve: /acp session/permission cancel-sentinel collision (${logSafe(sessionId)}, requestId ${logSafe(requestId)}): ${logSafe(err.message)}`
              );
              if (id !== void 0) {
                conn.sendConn(
                  error(id, RPC.INTERNAL_ERROR, err.message, {
                    httpStatus: 500,
                    code: "cancel_sentinel_collision",
                    requestId: err.requestId,
                    sentinel: err.sentinel
                  })
                );
              }
              return;
            }
            writeStderrLine(
              `qwen serve: /acp session/permission vote failed (${logSafe(sessionId)}, requestId ${logSafe(requestId)}): ${logSafe(errMsg(err))}`
            );
            const cancelled = this.cancelAbandonedPermission(
              { sessionId, bridgeRequestId: requestId },
              pendingRef?.conn.sessions.get(sessionId)?.clientId
            );
            if (cancelled) {
              this.dropOwnPendingPermission(conn, requestId);
            }
            throw err;
          }
          if (!accepted) {
            writeStderrLine(
              `qwen serve: /acp session/permission vote not accepted by bridge (${logSafe(sessionId)}, requestId ${logSafe(requestId)})`
            );
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "Permission vote not accepted (request already resolved)",
                  {
                    httpStatus: 409,
                    sessionId,
                    requestId
                  }
                )
              );
            }
            return;
          }
          this.dropOwnPendingPermission(conn, requestId);
          writeStderrLine(
            `qwen serve: /acp session/permission vote accepted (${logSafe(sessionId)}, requestId ${logSafe(requestId)}, connection ${logSafe(conn.connectionId.slice(0, 8))})`
          );
          this.replyConn(conn, id, {});
          return;
        }
        // STANDARD method (SDK 0.14.1, non-`unstable_`): model + mode live
        // here under categories `model`/`mode`, routed to the existing bridge
        // setters. Replaces the old vendor `_qwen/session/set_model`.
        case "session/set_config_option": {
          const sessionId = String(params["sessionId"] ?? "");
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const configId = String(params["configId"] ?? "");
            const rawValue = params["value"];
            const ctx = this.sessionCtx(conn, sessionId, loopback);
            if (typeof rawValue !== "string" || rawValue.length === 0) {
              if (id !== void 0) {
                this.replySession(
                  conn,
                  sessionId,
                  id,
                  void 0,
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    "`value` must be a non-empty string"
                  )
                );
              }
              return;
            }
            if (configId === "model") {
              await this.bridge.setSessionModel(
                sessionId,
                { modelId: rawValue },
                ctx
              );
            } else if (configId === "mode") {
              if (!APPROVAL_MODES.includes(rawValue)) {
                if (id !== void 0) {
                  this.replySession(
                    conn,
                    sessionId,
                    id,
                    void 0,
                    error(
                      id,
                      RPC.INVALID_PARAMS,
                      `invalid mode "${rawValue}" (expected one of: ${APPROVAL_MODES.join(", ")})`
                    )
                  );
                }
                return;
              }
              await this.bridge.setSessionApprovalMode(
                sessionId,
                rawValue,
                { persist: params["persist"] === true },
                ctx
              );
            } else {
              if (id !== void 0) {
                this.replySession(
                  conn,
                  sessionId,
                  id,
                  void 0,
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    `Unknown configId: ${configId}`
                  )
                );
              }
              return;
            }
            const configOptions = await this.configOptionsFor(sessionId);
            this.replySession(conn, sessionId, id, { configOptions });
          });
          return;
        }
        // ACP standard: session/set_mode — dedicated method for mode changes.
        // Maps to the same bridge call as set_config_option with configId='mode'.
        case "session/set_mode": {
          const sessionId = String(params["sessionId"] ?? "");
          if (!sessionId) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`sessionId` is required")
              );
            return;
          }
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const modeId = String(params["modeId"] ?? "");
            if (!modeId || !APPROVAL_MODES.includes(modeId)) {
              if (id !== void 0) {
                this.replySession(
                  conn,
                  sessionId,
                  id,
                  void 0,
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    `invalid modeId "${modeId}" (expected one of: ${APPROVAL_MODES.join(", ")})`
                  )
                );
              }
              return;
            }
            const ctx = this.sessionCtx(conn, sessionId, loopback);
            await this.bridge.setSessionApprovalMode(
              sessionId,
              modeId,
              { persist: false },
              ctx
            );
            this.replySession(conn, sessionId, id, {});
          });
          return;
        }
        // ACP standard (unstable): session/set_model — dedicated method for
        // model changes. Maps to the same bridge call as set_config_option
        // with configId='model'.
        case "session/set_model": {
          const sessionId = String(params["sessionId"] ?? "");
          if (!sessionId) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`sessionId` is required")
              );
            return;
          }
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const modelId = String(params["modelId"] ?? "");
            if (!modelId) {
              if (id !== void 0) {
                this.replySession(
                  conn,
                  sessionId,
                  id,
                  void 0,
                  error(id, RPC.INVALID_PARAMS, "`modelId` is required")
                );
              }
              return;
            }
            const ctx = this.sessionCtx(conn, sessionId, loopback);
            await this.bridge.setSessionModel(
              sessionId,
              { modelId, sessionId },
              ctx
            );
            this.replySession(conn, sessionId, id, {});
          });
          return;
        }
        case `${QWEN_METHOD_NS}session/heartbeat`: {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.requireOwned(conn, sessionId, id)) return;
          const result = this.bridge.recordHeartbeat(
            sessionId,
            this.sessionCtx(conn, sessionId, loopback)
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}session/context`: {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.requireOwned(conn, sessionId, id)) return;
          this.replyConn(
            conn,
            id,
            await this.bridge.getSessionContextStatus(sessionId)
          );
          return;
        }
        case `${QWEN_METHOD_NS}session/supported_commands`: {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.requireOwned(conn, sessionId, id)) return;
          this.replyConn(
            conn,
            id,
            await this.bridge.getSessionSupportedCommandsStatus(sessionId)
          );
          return;
        }
        case `${QWEN_METHOD_NS}session/update_metadata`: {
          const sessionId = String(params["sessionId"] ?? "");
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const metadata = isObject(params["metadata"]) ? params["metadata"] : {};
            const result = this.bridge.updateSessionMetadata(
              sessionId,
              metadata,
              this.sessionCtx(conn, sessionId, loopback)
            );
            this.replyConn(conn, id, result);
          });
          return;
        }
        case `${QWEN_METHOD_NS}workspace/mcp`:
          this.replyConn(
            conn,
            id,
            await this.workspace.getWorkspaceMcpStatus(
              this.wsCtx(conn, method)
            )
          );
          return;
        case `${QWEN_METHOD_NS}workspace/skills`:
          this.replyConn(
            conn,
            id,
            await this.workspace.getWorkspaceSkillsStatus(
              this.wsCtx(conn, method)
            )
          );
          return;
        case `${QWEN_METHOD_NS}workspace/providers`:
          this.replyConn(
            conn,
            id,
            await this.workspace.getWorkspaceProvidersStatus(
              this.wsCtx(conn, method)
            )
          );
          return;
        case `${QWEN_METHOD_NS}workspace/env`:
          this.replyConn(
            conn,
            id,
            await this.workspace.getWorkspaceEnvStatus(
              this.wsCtx(conn, method)
            )
          );
          return;
        case `${QWEN_METHOD_NS}workspace/preflight`:
          this.replyConn(
            conn,
            id,
            await this.workspace.getWorkspacePreflightStatus(
              this.wsCtx(conn, method)
            )
          );
          return;
        case `${QWEN_METHOD_NS}workspace/init`: {
          const rawForce = params["force"];
          if (rawForce !== void 0 && typeof rawForce !== "boolean") {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`force` must be a boolean when provided"
                )
              );
            }
            return;
          }
          const force = rawForce === true;
          const result = await this.workspace.initWorkspace(
            this.wsCtx(conn, method),
            { force }
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/trust`: {
          const result = await this.workspace.getWorkspaceTrustStatus(
            this.wsCtx(conn, method)
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/trust/request`: {
          const desiredState = params["desiredState"];
          if (desiredState !== "trusted" && desiredState !== "untrusted") {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  '`desiredState` must be "trusted" or "untrusted"'
                )
              );
            }
            return;
          }
          const reason = params["reason"];
          if (reason !== void 0 && (typeof reason !== "string" || reason.length > MAX_TRUST_REASON_LENGTH)) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`reason\` must be a string up to ${MAX_TRUST_REASON_LENGTH} chars`
                )
              );
            }
            return;
          }
          const ctx = this.wsCtx(conn, method);
          const status = await this.workspace.getWorkspaceTrustStatus(ctx);
          if (!status.folderTrustEnabled) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_REQUEST,
                  "Folder trust is disabled for this workspace"
                )
              );
            }
            return;
          }
          const result = await this.workspace.requestWorkspaceTrustChange(ctx, {
            desiredState,
            ...reason !== void 0 ? { reason } : {}
          });
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/permissions`: {
          const result = await this.workspace.getWorkspacePermissionsStatus(
            this.wsCtx(conn, method)
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/permissions/set`: {
          const scope = params["scope"];
          if (scope !== "user" && scope !== "workspace") {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  '`scope` must be "user" or "workspace"'
                )
              );
            }
            return;
          }
          const ruleType = params["ruleType"];
          if (!isPermissionRuleType(ruleType)) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  '`ruleType` must be "allow", "ask", or "deny"'
                )
              );
            }
            return;
          }
          let rules;
          try {
            const settings = loadSettings(this.boundWorkspace);
            const scopeSettings = scope === "workspace" ? settings.workspace.settings : settings.user.settings;
            const existingRules = readPermissionRuleSet(scopeSettings)[ruleType];
            rules = normalizePermissionRules(params["rules"], {
              existingRules
            });
          } catch (err) {
            if (err instanceof PermissionRulesValidationError) {
              if (id !== void 0) {
                conn.sendConn(error(id, RPC.INVALID_PARAMS, err.message));
              }
              return;
            }
            throw err;
          }
          let result;
          try {
            result = await this.workspace.setWorkspacePermissionRules(
              this.wsCtx(conn, method),
              { scope, ruleType, rules }
            );
          } catch (err) {
            if (err instanceof WorkspacePermissionRulesSessionRequiredError && id !== void 0) {
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, err.message, {
                  errorKind: "permission_session_required"
                })
              );
              return;
            }
            throw err;
          }
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/voice`: {
          const result = await this.workspace.getWorkspaceVoiceStatus(
            this.wsCtx(conn, method)
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/voice/set`: {
          const update = parseWorkspaceVoiceUpdateParams(params);
          if ("error" in update) {
            if (id !== void 0) {
              conn.sendConn(error(id, RPC.INVALID_PARAMS, update.error));
            }
            return;
          }
          const result = await this.workspace.setWorkspaceVoiceSettings(
            this.wsCtx(conn, method),
            update
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/setup-github`: {
          if (params["consent"] !== true) {
            if (id !== void 0) {
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`consent` must be true", {
                  errorKind: "github_setup_consent_required"
                })
              );
            }
            return;
          }
          if (!this.fsFactory) {
            if (id !== void 0) {
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured", {
                  errorKind: "internal_error"
                })
              );
            }
            return;
          }
          try {
            const result = await setupGithub({
              cwd: this.boundWorkspace,
              workspaceRoot: this.boundWorkspace,
              proxy: resolveSetupGithubProxy(this.boundWorkspace),
              abortSignal: conn.abortSignal,
              fileOps: createSetupGithubFileOps(
                this.fsFactory,
                `ACP ${method}`,
                conn.clientId
              )
            });
            this.bridge.publishWorkspaceEvent({
              type: "github_setup_completed",
              data: setupGithubEventData(result),
              ...conn.clientId ? { originatorClientId: conn.clientId } : {}
            });
            this.replyConn(conn, id, result);
          } catch (err) {
            if (err instanceof SetupGithubError && id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  err.status >= 500 ? RPC.INTERNAL_ERROR : RPC.INVALID_PARAMS,
                  sanitizeSetupGithubMessage(err.message, this.boundWorkspace),
                  {
                    errorKind: err.code,
                    ...err.partial ? {
                      partial: true,
                      result: err.partialResult ? sanitizeSetupGithubResult(
                        err.partialResult,
                        this.boundWorkspace
                      ) : null
                    } : {}
                  }
                )
              );
              return;
            }
            throw err;
          }
          return;
        }
        case `${QWEN_METHOD_NS}workspace/set_tool_enabled`: {
          const toolName = String(params["toolName"] ?? "");
          if (!toolName || toolName.length > MAX_NAME_LENGTH) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`toolName\` is required and must be \u2264 ${MAX_NAME_LENGTH} chars`
                )
              );
            }
            return;
          }
          const result = await this.workspace.setWorkspaceToolEnabled(
            this.wsCtx(conn, method),
            toolName,
            params["enabled"] === true
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/restart_mcp_server`: {
          const serverName = String(params["serverName"] ?? "");
          if (!serverName || serverName.length > MAX_NAME_LENGTH) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`serverName\` is required and must be \u2264 ${MAX_NAME_LENGTH} chars`
                )
              );
            }
            return;
          }
          const rawIdx = params["entryIndex"];
          if (rawIdx !== void 0 && (typeof rawIdx !== "number" || !Number.isInteger(rawIdx) || rawIdx < 0)) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`entryIndex` must be a non-negative integer"
                )
              );
            }
            return;
          }
          const result = await this.workspace.restartMcpServer(
            this.wsCtx(conn, method),
            serverName,
            rawIdx !== void 0 ? { entryIndex: rawIdx } : void 0
          );
          this.replyConn(conn, id, result);
          return;
        }
        // ── Wave 1+2: ACP/REST parity methods ───────────────────────
        case `${QWEN_METHOD_NS}session/recap`: {
          const sessionId = String(params["sessionId"] ?? "");
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const result = await this.bridge.generateSessionRecap(
              sessionId,
              this.sessionCtx(conn, sessionId, loopback)
            );
            this.replyConn(conn, id, result);
          });
          return;
        }
        case `${QWEN_METHOD_NS}session/btw`: {
          const sessionId = String(params["sessionId"] ?? "");
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const rawQ = params["question"];
            if (typeof rawQ !== "string" || rawQ.trim().length === 0 || rawQ.length > BTW_MAX_INPUT_LENGTH) {
              if (id !== void 0)
                conn.sendConn(
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    `\`question\` required, non-empty, max ${BTW_MAX_INPUT_LENGTH} chars`
                  )
                );
              return;
            }
            const result = await this.bridge.generateSessionBtw(
              sessionId,
              rawQ.trim(),
              void 0,
              this.sessionCtx(conn, sessionId, loopback)
            );
            this.replyConn(conn, id, result);
          });
          return;
        }
        case `${QWEN_METHOD_NS}session/shell`: {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.sessionShellCommandEnabled) {
            if (id !== void 0) {
              conn.sendConn(rpcErrorFrame(id, new SessionShellDisabledError()));
            }
            return;
          }
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const binding = conn.sessions.get(sessionId);
            const clientId = binding?.clientId;
            if (!clientId) {
              if (id !== void 0) {
                conn.sendConn(
                  rpcErrorFrame(id, new SessionShellClientRequiredError())
                );
              }
              return;
            }
            const rawCmd = params["command"];
            if (typeof rawCmd !== "string" || rawCmd.trim().length === 0) {
              if (id !== void 0)
                conn.sendConn(
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    "`command` required and must be non-empty"
                  )
                );
              return;
            }
            const logSessionId = logSafe(sessionId.slice(0, 8));
            const logClientId = logSafe(String(conn.clientId?.slice(0, 8)));
            const logCommand = logSafe(rawCmd.slice(0, 120));
            writeStderrLine(
              `qwen serve: /acp session/shell session=${logSessionId} client=${logClientId} cmd=${logCommand}`
            );
            const result = await this.bridge.executeShellCommand(
              sessionId,
              rawCmd,
              binding.abort.signal,
              this.sessionCtx(conn, sessionId, loopback)
            );
            this.replyConn(conn, id, result);
          });
          return;
        }
        case `${QWEN_METHOD_NS}session/detach`: {
          const sessionId = String(params["sessionId"] ?? "");
          await this.withMutableOwned(conn, sessionId, id, async () => {
            const ctx = this.sessionCtx(conn, sessionId, loopback);
            await this.bridge.detachClient(sessionId, ctx.clientId);
            this.replyConn(conn, id, { ok: true });
          });
          return;
        }
        case `${QWEN_METHOD_NS}session/context_usage`: {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.requireOwned(conn, sessionId, id)) return;
          const result = await this.bridge.getSessionContextUsageStatus(
            sessionId,
            { detail: params["detail"] === true }
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}session/tasks`: {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.requireOwned(conn, sessionId, id)) return;
          const result = await this.bridge.getSessionTasksStatus(sessionId);
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}session/lsp`: {
          const sessionId = String(params["sessionId"] ?? "");
          if (!this.requireOwned(conn, sessionId, id)) return;
          const result = await this.bridge.getSessionLspStatus(sessionId);
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/memory`: {
          const result = await collectWorkspaceMemoryStatus(
            this.boundWorkspace
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/memory/write`: {
          const content = params["content"];
          if (typeof content !== "string") {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`content` required, must be string"
                )
              );
            return;
          }
          if (Buffer.byteLength(content, "utf8") > 1024 * 1024) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`content` exceeds 1MB limit")
              );
            return;
          }
          const rawScope = params["scope"];
          if (rawScope !== void 0 && rawScope !== "workspace" && rawScope !== "global") {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  '`scope` must be "workspace" or "global"'
                )
              );
            return;
          }
          const scope = rawScope ?? "workspace";
          const rawMode = params["mode"];
          if (rawMode !== void 0 && rawMode !== "append" && rawMode !== "replace") {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  '`mode` must be "append" or "replace"'
                )
              );
            return;
          }
          const mode = rawMode ?? "append";
          writeStderrLine(
            `qwen serve: /acp workspace/memory/write scope=${scope} mode=${mode} client=${conn.clientId?.slice(0, 8)} bytes=${Buffer.byteLength(content, "utf8")}`
          );
          const wr = await writeWorkspaceContextFile({
            scope,
            mode,
            content,
            projectRoot: this.boundWorkspace
          });
          this.replyConn(conn, id, {
            ok: true,
            filePath: wr.filePath,
            bytesWritten: wr.bytesWritten,
            changed: wr.changed
          });
          if (wr.changed) {
            try {
              this.bridge.publishWorkspaceEvent({
                type: "memory_changed",
                data: {
                  scope,
                  filePath: wr.filePath,
                  mode,
                  bytesWritten: wr.bytesWritten
                },
                originatorClientId: conn.clientId
              });
            } catch {
            }
          }
          return;
        }
        case `${QWEN_METHOD_NS}workspace/memory/remember`: {
          const content = params["content"];
          if (typeof content !== "string" || !content.trim()) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`content` must be a non-empty string"
                )
              );
            }
            return;
          }
          if (Buffer.byteLength(content, "utf8") > MAX_REMEMBER_CONTENT_BYTES) {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`content\` exceeds the ${MAX_REMEMBER_CONTENT_BYTES}-byte limit`
                )
              );
            }
            return;
          }
          const rawContextMode = params["contextMode"] ?? "workspace";
          if (rawContextMode !== "workspace" && rawContextMode !== "clean") {
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  '`contextMode` must be "workspace", "clean", or omitted'
                )
              );
            }
            return;
          }
          try {
            const available = await this.bridge.isWorkspaceMemoryRememberAvailable();
            if (!available) {
              if (id !== void 0) {
                conn.sendConn(
                  error(
                    id,
                    -32009,
                    "Managed memory is unavailable for this daemon workspace",
                    {
                      errorKind: "managed_memory_unavailable",
                      httpStatus: 409
                    }
                  )
                );
              }
              return;
            }
            const task = this.workspaceRememberLane.enqueue({
              content: content.trim(),
              contextMode: rawContextMode,
              ...conn.clientId ? { originatorClientId: conn.clientId } : {}
            });
            this.replyConn(conn, id, task);
          } catch (err) {
            const code = extractRememberErrorCode(err);
            if (id !== void 0) {
              conn.sendConn(
                error(
                  id,
                  -32099,
                  code === "remember_queue_full" ? "Workspace memory remember queue is full." : code === "managed_memory_unavailable" ? "Managed memory is unavailable for this daemon workspace" : "Workspace memory remember failed.",
                  {
                    errorKind: code,
                    httpStatus: code === "remember_queue_full" ? 429 : code === "managed_memory_unavailable" ? 409 : 500
                  }
                )
              );
            }
          }
          return;
        }
        case `${QWEN_METHOD_NS}workspace/memory/remember/get`: {
          const taskId = params["taskId"];
          if (typeof taskId !== "string" || taskId.length === 0) {
            if (id !== void 0) {
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`taskId` required"));
            }
            return;
          }
          const task = this.workspaceRememberLane.get(taskId, conn.clientId);
          if (!task) {
            if (id !== void 0) {
              conn.sendConn(
                error(id, -32004, "Workspace memory remember task not found", {
                  errorKind: "remember_task_not_found",
                  httpStatus: 404
                })
              );
            }
            return;
          }
          this.replyConn(conn, id, task);
          return;
        }
        case `${QWEN_METHOD_NS}file/read`: {
          const p = String(params["path"] ?? "");
          if (!p) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`path` required"));
            return;
          }
          if (!this.fsFactory) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured")
              );
            return;
          }
          const fs3 = this.fsFactory.forRequest({
            originatorClientId: conn.clientId,
            route: `ACP ${method}`
          });
          const maxBytes = parseOptionalSafeIntegerInRange(
            params["maxBytes"],
            1,
            MAX_READ_BYTES
          );
          if (maxBytes === null) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`maxBytes\` must be a positive integer in [1, ${MAX_READ_BYTES}]`
                )
              );
            return;
          }
          const line = parseOptionalSafeIntegerInRange(
            params["line"],
            1,
            Number.MAX_SAFE_INTEGER
          );
          if (line === null) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`line` must be a positive integer"
                )
              );
            return;
          }
          const limit = parseOptionalSafeIntegerInRange(
            params["limit"],
            1,
            MAX_FILE_LINE_LIMIT2
          );
          if (limit === null) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`limit\` must be a positive integer in [1, ${MAX_FILE_LINE_LIMIT2}]`
                )
              );
            return;
          }
          const resolved = await fs3.resolve(p, "read");
          const out = await fs3.readText(resolved, { maxBytes, line, limit });
          this.replyConn(conn, id, {
            path: p,
            content: out.content,
            ...out.meta
          });
          return;
        }
        case `${QWEN_METHOD_NS}file/read_bytes`: {
          if (!this.fsFactory) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured")
              );
            return;
          }
          const p = String(params["path"] ?? "");
          if (!p) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`path` required"));
            return;
          }
          const fs3 = this.fsFactory.forRequest({
            originatorClientId: conn.clientId,
            route: `ACP ${method}`
          });
          const offset = parseOptionalSafeIntegerInRange(
            params["offset"],
            0,
            Number.MAX_SAFE_INTEGER
          );
          if (offset === null) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`offset` must be a non-negative safe integer"
                )
              );
            return;
          }
          const maxBytes = parseOptionalSafeIntegerInRange(
            params["maxBytes"],
            1,
            MAX_READ_BYTES
          );
          if (maxBytes === null) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`maxBytes\` must be a positive integer in [1, ${MAX_READ_BYTES}]`
                )
              );
            return;
          }
          const resolved = await fs3.resolve(p, "read");
          const buf = await fs3.readBytesWindow(resolved, { offset, maxBytes });
          this.replyConn(conn, id, { path: p, ...buf });
          return;
        }
        case `${QWEN_METHOD_NS}file/stat`: {
          if (!this.fsFactory) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured")
              );
            return;
          }
          const p = String(params["path"] ?? "");
          if (!p) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`path` required"));
            return;
          }
          const fs3 = this.fsFactory.forRequest({
            originatorClientId: conn.clientId,
            route: `ACP ${method}`
          });
          const resolved = await fs3.resolve(p, "read");
          const result = await fs3.stat(resolved);
          this.replyConn(conn, id, { path: p, ...result });
          return;
        }
        case `${QWEN_METHOD_NS}file/list`: {
          if (!this.fsFactory) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured")
              );
            return;
          }
          const p = String(params["path"] ?? "");
          if (!p) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`path` required"));
            return;
          }
          const fs3 = this.fsFactory.forRequest({
            originatorClientId: conn.clientId,
            route: `ACP ${method}`
          });
          const resolved = await fs3.resolve(p, "read");
          const MAX_LIST = 2e3;
          const entries = await fs3.list(resolved, { maxEntries: MAX_LIST + 1 });
          const truncated = entries.length > MAX_LIST;
          this.replyConn(conn, id, {
            path: p,
            entries: truncated ? entries.slice(0, MAX_LIST) : entries,
            truncated
          });
          return;
        }
        case `${QWEN_METHOD_NS}file/glob`: {
          const pattern = String(params["pattern"] ?? "");
          if (!pattern) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`pattern` required")
              );
            return;
          }
          if (!this.fsFactory) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured")
              );
            return;
          }
          const fs3 = this.fsFactory.forRequest({
            originatorClientId: conn.clientId,
            route: `ACP ${method}`
          });
          const maxResults = parseOptionalPositiveInteger(
            params["maxResults"],
            DEFAULT_FILE_GLOB_MAX_RESULTS,
            MAX_FILE_GLOB_MAX_RESULTS
          );
          if (maxResults === null) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`maxResults` must be an integer between 1 and 50000"
                )
              );
            return;
          }
          const matches = await fs3.glob(pattern, {
            maxResults: maxResults + 1
          });
          const truncated = matches.length > maxResults;
          this.replyConn(conn, id, {
            pattern,
            matches: truncated ? matches.slice(0, maxResults) : matches,
            truncated
          });
          return;
        }
        case `${QWEN_METHOD_NS}file/write`: {
          const p = String(params["path"] ?? "");
          if (!p) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`path` required"));
            return;
          }
          if (typeof params["content"] !== "string") {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`content` must be string")
              );
            return;
          }
          if (!this.fsFactory) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured")
              );
            return;
          }
          const fs3 = this.fsFactory.forRequest({
            originatorClientId: conn.clientId,
            route: `ACP ${method}`
          });
          const resolved = await fs3.resolve(p, "write");
          if (Buffer.byteLength(params["content"], "utf8") > 10 * 1024 * 1024) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "content exceeds 10MB limit")
              );
            return;
          }
          await fs3.writeTextOverwrite(resolved, params["content"]);
          this.replyConn(conn, id, { ok: true, path: p });
          return;
        }
        case `${QWEN_METHOD_NS}file/edit`: {
          const p = String(params["path"] ?? "");
          if (!p) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`path` required"));
            return;
          }
          if (typeof params["oldText"] !== "string" || typeof params["newText"] !== "string") {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`oldText` and `newText` must be strings"
                )
              );
            return;
          }
          if (!this.fsFactory) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "File system not configured")
              );
            return;
          }
          const fs3 = this.fsFactory.forRequest({
            originatorClientId: conn.clientId,
            route: `ACP ${method}`
          });
          const resolved = await fs3.resolve(p, "write");
          const result = await fs3.edit(
            resolved,
            params["oldText"],
            params["newText"]
          );
          this.replyConn(conn, id, { ok: true, path: p, ...result });
          return;
        }
        case `${QWEN_METHOD_NS}workspace/auth/status`: {
          if (!this.deviceFlowRegistry) {
            this.replyConn(conn, id, { pendingDeviceFlows: [] });
            return;
          }
          const pending = this.deviceFlowRegistry.listPending();
          const projected = pending.map((v) => ({
            deviceFlowId: v.deviceFlowId,
            providerId: v.providerId,
            expiresAt: v.expiresAt
          }));
          this.replyConn(conn, id, { pendingDeviceFlows: projected });
          return;
        }
        case `${QWEN_METHOD_NS}workspace/auth/device_flow/start`: {
          if (!this.deviceFlowRegistry) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "Device flow not configured")
              );
            return;
          }
          const providerId = String(params["providerId"] ?? "");
          if (!providerId) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`providerId` required")
              );
            return;
          }
          const startResult = await this.deviceFlowRegistry.start({
            providerId,
            initiatorClientId: conn.clientId
          });
          const { view, attached } = startResult;
          const gated = view.initiatorClientId === conn.clientId ? view : {
            deviceFlowId: view.deviceFlowId,
            providerId: view.providerId,
            status: view.status,
            expiresAt: view.expiresAt
          };
          this.replyConn(conn, id, { view: gated, attached });
          return;
        }
        case `${QWEN_METHOD_NS}workspace/auth/device_flow/get`: {
          if (!this.deviceFlowRegistry) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "Device flow not configured")
              );
            return;
          }
          const flowId = String(params["id"] ?? "");
          if (!flowId) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`id` required"));
            return;
          }
          const view = this.deviceFlowRegistry.get(flowId);
          if (!view) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `Device flow "${flowId}" not found`
                )
              );
            return;
          }
          const gated = view.initiatorClientId === conn.clientId ? view : {
            deviceFlowId: view.deviceFlowId,
            providerId: view.providerId,
            status: view.status,
            expiresAt: view.expiresAt
          };
          this.replyConn(conn, id, gated);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/auth/device_flow/cancel`: {
          if (!this.deviceFlowRegistry) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INTERNAL_ERROR, "Device flow not configured")
              );
            return;
          }
          const flowId = String(params["id"] ?? "");
          if (!flowId) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`id` required"));
            return;
          }
          const flowView = this.deviceFlowRegistry.get(flowId);
          if (flowView && flowView.initiatorClientId && flowView.initiatorClientId !== conn.clientId) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "Only the flow initiator can cancel"
                )
              );
            return;
          }
          const cancelResult = this.deviceFlowRegistry.cancel(
            flowId,
            conn.clientId
          );
          if (!cancelResult) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `Device flow "${flowId}" not found`
                )
              );
            return;
          }
          this.replyConn(conn, id, {
            ok: true,
            alreadyTerminal: cancelResult.alreadyTerminal
          });
          return;
        }
        case `${QWEN_METHOD_NS}workspace/tools`: {
          const result = await this.bridge.getWorkspaceToolsStatus();
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/mcp/tools`: {
          const serverName = String(params["serverName"] ?? "");
          if (!serverName) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`serverName` required")
              );
            return;
          }
          const result = await this.bridge.getWorkspaceMcpToolsStatus(serverName);
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/mcp/resources`: {
          const serverName = String(params["serverName"] ?? "");
          if (!serverName) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`serverName` required")
              );
            return;
          }
          const result = await this.bridge.getWorkspaceMcpResourcesStatus(serverName);
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/mcp/servers/add`: {
          const name = String(params["name"] ?? "");
          if (!name || name.length > MAX_NAME_LENGTH) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`name\` required, max ${MAX_NAME_LENGTH} chars`
                )
              );
            return;
          }
          const config = params["config"];
          if (!config || typeof config !== "object" || Array.isArray(config)) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "`config` required, must be object"
                )
              );
            return;
          }
          const result = await this.bridge.addRuntimeMcpServer(
            name,
            config,
            conn.clientId
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}workspace/mcp/servers/remove`: {
          const name = String(params["name"] ?? "");
          if (!name || name.length > MAX_NAME_LENGTH) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `\`name\` required, max ${MAX_NAME_LENGTH} chars`
                )
              );
            return;
          }
          const result = await this.bridge.removeRuntimeMcpServer(
            name,
            conn.clientId
          );
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}sessions/delete`: {
          const ids = this.parseSessionIds(params);
          const svc = new SessionService(this.boundWorkspace);
          const result = await deleteDaemonSessions({
            sessionIds: ids,
            service: svc,
            bridge: this.bridge,
            coordinator: this.archiveCoordinator,
            onError: /* @__PURE__ */ __name(({ phase, sessionId, error: error2 }) => {
              const safeSessionId = logSafe(sessionId.slice(0, 8));
              const safeMessage = logSafe(error2);
              writeStderrLine(
                `qwen serve: /acp sessions/delete ${phase}Session(${safeSessionId}) failed: ${safeMessage}`
              );
            }, "onError")
          });
          this.replyConn(conn, id, result);
          return;
        }
        case `${QWEN_METHOD_NS}sessions/archive`: {
          const ids = this.parseSessionIds(params);
          const svc = new SessionService(this.boundWorkspace, {
            onWarning: logSessionArchiveWarning
          });
          const result = await archiveDaemonSessions({
            sessionIds: ids,
            service: svc,
            bridge: this.bridge,
            coordinator: this.archiveCoordinator
          });
          this.replyConn(conn, id, {
            archived: result.archived,
            alreadyArchived: result.alreadyArchived,
            notFound: result.notFound,
            errors: this.serializeSessionErrors(result.errors)
          });
          return;
        }
        case `${QWEN_METHOD_NS}sessions/unarchive`: {
          const ids = this.parseSessionIds(params);
          const svc = new SessionService(this.boundWorkspace, {
            onWarning: logSessionArchiveWarning
          });
          const result = await unarchiveDaemonSessions({
            sessionIds: ids,
            service: svc,
            coordinator: this.archiveCoordinator
          });
          this.replyConn(conn, id, {
            unarchived: result.unarchived,
            alreadyActive: result.alreadyActive,
            notFound: result.notFound,
            errors: this.serializeSessionErrors(result.errors)
          });
          return;
        }
        case `${QWEN_METHOD_NS}workspace/agents/list`: {
          const agents = await this.agentManager.listSubagents({ force: true });
          this.replyConn(conn, id, {
            v: 1,
            workspaceCwd: this.boundWorkspace,
            agents: agents.map(toSummary)
          });
          return;
        }
        case `${QWEN_METHOD_NS}workspace/agents/get`: {
          const agentType = String(params["agentType"] ?? "");
          if (!agentType) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`agentType` required")
              );
            return;
          }
          const config = await this.agentManager.loadSubagent(agentType);
          if (!config) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, `Agent "${agentType}" not found`)
              );
            return;
          }
          this.replyConn(conn, id, toDetail(config));
          return;
        }
        case `${QWEN_METHOD_NS}workspace/agents/create`: {
          const scope = params["scope"];
          if (scope !== "workspace" && scope !== "global") {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  '`scope` must be "workspace" or "global"'
                )
              );
            return;
          }
          const name = params["name"];
          if (typeof name !== "string" || !name.trim()) {
            if (id !== void 0)
              conn.sendConn(error(id, RPC.INVALID_PARAMS, "`name` required"));
            return;
          }
          const level = scope === "workspace" ? "project" : "user";
          if (BuiltinAgentRegistry.isBuiltinAgent(name)) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  `Cannot shadow built-in agent "${name}"`
                )
              );
            return;
          }
          const collision = await this.agentManager.loadSubagent(name, level);
          if (collision) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, `Agent "${name}" already exists`)
              );
            return;
          }
          await this.agentManager.createSubagent(
            {
              name,
              level,
              description: typeof params["description"] === "string" ? params["description"] : "",
              systemPrompt: typeof params["systemPrompt"] === "string" ? params["systemPrompt"] : "",
              tools: Array.isArray(params["tools"]) ? params["tools"] : void 0,
              model: typeof params["model"] === "string" ? params["model"] : void 0
            },
            { level }
          );
          const created = await this.agentManager.loadSubagent(name, level);
          this.replyConn(conn, id, {
            ok: true,
            agent: created ? toDetail(created) : null
          });
          try {
            this.bridge.publishWorkspaceEvent({
              type: "agent_changed",
              data: { change: "created", name, level },
              originatorClientId: conn.clientId
            });
          } catch {
          }
          return;
        }
        case `${QWEN_METHOD_NS}workspace/agents/update`: {
          const agentType = String(params["agentType"] ?? "");
          if (!agentType) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`agentType` required")
              );
            return;
          }
          const existing = await this.agentManager.loadSubagent(agentType);
          if (!existing) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, `Agent "${agentType}" not found`)
              );
            return;
          }
          const MAX_FIELD_BYTES = 256 * 1024;
          const updates = {};
          if (typeof params["description"] === "string") {
            if (Buffer.byteLength(params["description"], "utf8") > MAX_FIELD_BYTES) {
              if (id !== void 0)
                conn.sendConn(
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    "`description` exceeds 256KB limit"
                  )
                );
              return;
            }
            updates["description"] = params["description"];
          }
          if (typeof params["systemPrompt"] === "string") {
            if (Buffer.byteLength(params["systemPrompt"], "utf8") > MAX_FIELD_BYTES) {
              if (id !== void 0)
                conn.sendConn(
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    "`systemPrompt` exceeds 256KB limit"
                  )
                );
              return;
            }
            updates["systemPrompt"] = params["systemPrompt"];
          }
          if (Array.isArray(params["tools"])) {
            if (params["tools"].length > 256) {
              if (id !== void 0)
                conn.sendConn(
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    "`tools` exceeds 256-entry limit"
                  )
                );
              return;
            }
            if (!params["tools"].every(
              (t) => typeof t === "string" && t.length <= 256
            )) {
              if (id !== void 0)
                conn.sendConn(
                  error(
                    id,
                    RPC.INVALID_PARAMS,
                    "`tools` elements must be strings \u2264256 chars"
                  )
                );
              return;
            }
            updates["tools"] = params["tools"];
          }
          if (typeof params["model"] === "string")
            updates["model"] = params["model"];
          if (Object.keys(updates).length === 0) {
            if (id !== void 0)
              conn.sendConn(
                error(
                  id,
                  RPC.INVALID_PARAMS,
                  "at least one updatable field required"
                )
              );
            return;
          }
          await this.agentManager.updateSubagent(
            agentType,
            updates,
            existing.level
          );
          const updated = await this.agentManager.loadSubagent(
            agentType,
            existing.level
          );
          this.replyConn(conn, id, {
            ok: true,
            agent: updated ? toDetail(updated) : null
          });
          try {
            this.bridge.publishWorkspaceEvent({
              type: "agent_changed",
              data: {
                change: "updated",
                name: agentType,
                level: existing.level
              },
              originatorClientId: conn.clientId
            });
          } catch {
          }
          return;
        }
        case `${QWEN_METHOD_NS}workspace/agents/delete`: {
          const agentType = String(params["agentType"] ?? "");
          if (!agentType) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, "`agentType` required")
              );
            return;
          }
          const scope = typeof params["scope"] === "string" ? params["scope"] : void 0;
          const level = scope === "workspace" ? "project" : scope === "global" ? "user" : void 0;
          const existing = await this.agentManager.loadSubagent(
            agentType,
            level
          );
          if (!existing) {
            if (id !== void 0)
              conn.sendConn(
                error(id, RPC.INVALID_PARAMS, `Agent "${agentType}" not found`)
              );
            return;
          }
          await this.agentManager.deleteSubagent(agentType, existing.level);
          this.replyConn(conn, id, { ok: true });
          try {
            this.bridge.publishWorkspaceEvent({
              type: "agent_changed",
              data: {
                change: "deleted",
                name: agentType,
                level: existing.level
              },
              originatorClientId: conn.clientId
            });
          } catch {
          }
          return;
        }
        default:
          if (id !== void 0) {
            conn.sendConn(
              error(id, RPC.METHOD_NOT_FOUND, `Unknown method: ${method}`)
            );
          }
          return;
      }
    } catch (err) {
      writeStderrLine(
        `qwen serve: /acp dispatch error (${logSafe(method)}): ${logSafe(errMsg(err))}`
      );
      if (id !== void 0) {
        const { code, message, data } = toRpcError(err);
        const frame = error(id, code, message, data);
        const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
        if (sessionId && !CONN_ROUTED_METHODS.has(method)) {
          this.replySession(conn, sessionId, id, void 0, frame);
        } else {
          conn.sendConn(frame);
        }
      }
    }
  }
  /**
   * Bind a session-scoped SSE stream to the bridge's event stream,
   * translating each `BridgeEvent` into a JSON-RPC frame (design §4.2).
   */
  async pumpSessionEvents(conn, sessionId, signal, lastEventId) {
    try {
      const iterable = this.bridge.subscribeEvents(sessionId, {
        signal,
        ...lastEventId !== void 0 ? { lastEventId } : {}
      });
      let lastDeliveredId = lastEventId ?? 0;
      let sawEviction = false;
      for await (const event of iterable) {
        if (signal.aborted) break;
        conn.touch();
        this.translateEvent(conn, sessionId, event);
        if (event.type === "state_resync_required") sawEviction = true;
        if (typeof event.id === "number") {
          lastDeliveredId = event.id;
          conn.releaseDeferredSessionReplies(sessionId, event.id);
        }
        if (event.type === "replay_complete") {
          conn.endReplayDeferral(sessionId, lastDeliveredId, sawEviction);
          const replayedCount = event.data?.replayedCount;
          writeStderrLine(
            `qwen serve: /acp replay complete (${logSafe(sessionId)}) from=${lastEventId ?? "none"} delivered_through=${lastDeliveredId} count=${replayedCount ?? "n/a"} evicted=${sawEviction}`
          );
        }
      }
      if (!signal.aborted) conn.flushBufferedSessionFrames(sessionId);
    } catch (err) {
      if (!signal.aborted) {
        conn.flushBufferedSessionFrames(sessionId);
        conn.sendSession(
          sessionId,
          notification(`${QWEN_METHOD_NS}notify`, {
            kind: "stream_error",
            error: errMsg(err)
          })
        );
      }
      throw err;
    }
  }
  translateEvent(conn, sessionId, event) {
    switch (event.type) {
      case "session_update": {
        conn.sendSession(
          sessionId,
          notification("session/update", event.data),
          event.id
        );
        return;
      }
      case "permission_request": {
        const data = event.data;
        const binding = conn.sessions.get(sessionId);
        if (!binding?.stream || binding.stream.isClosed) {
          if (binding?.graceTimer) {
            writeStderrLine(
              `qwen serve: /acp permission cancel during reconnect grace (${logSafe(sessionId)}); vote not deferred (see \xA71.7 follow-up)`
            );
          }
          const cancelled = this.cancelAbandonedPermission(
            { sessionId, bridgeRequestId: data.requestId },
            // Pass the bridge-stamped clientId when the binding still exists
            // (stream closed but session live) — only `undefined` when the
            // session is fully gone.
            binding?.clientId
          );
          if (!cancelled) {
            writeStderrLine(
              `qwen serve: /acp permission cancel FAILED for ${logSafe(sessionId)} (mediator stuck; no automatic recovery)`
            );
          }
          return;
        }
        let id;
        for (const [existingId, p] of conn.pending) {
          if (p.kind === "permission" && p.sessionId === sessionId && p.bridgeRequestId === data.requestId) {
            id = existingId;
            break;
          }
        }
        if (id === void 0) {
          id = conn.nextId();
          conn.pending.set(id, {
            sessionId,
            bridgeRequestId: data.requestId,
            kind: "permission"
          });
        }
        void binding.stream.send(
          request(id, "session/request_permission", {
            sessionId: data.sessionId,
            toolCall: data.toolCall,
            options: data.options,
            _meta: { [QWEN_META_KEY]: { requestId: data.requestId } }
          }),
          // Carry the bus cursor: a permission request is a real sequenced
          // event, so the client must resume past it.
          event.id
        );
        return;
      }
      case "stream_error": {
        conn.sendSession(
          sessionId,
          notification(`${QWEN_METHOD_NS}notify`, {
            // Spread first so a stray `kind` in event.data can't shadow the
            // discriminator the client's error handler keys on.
            ...event.data,
            kind: "stream_error"
          }),
          // Pass the bus cursor through if present; a synthetic terminal frame
          // has no bus id (event.id undefined) so no SSE `id:` line is written.
          event.id
        );
        return;
      }
      default: {
        conn.sendSession(
          sessionId,
          notification(`${QWEN_METHOD_NS}notify`, {
            kind: event.type,
            data: event.data
          }),
          event.id
        );
      }
    }
  }
  /**
   * Resolve a client's JSON-RPC response to an agent→client request.
   * `fromLoopback` is the CURRENT request's loopback bit (the vote POST may
   * arrive from a different peer than `initialize`).
   */
  resolveClientResponse(conn, msg, fromLoopback) {
    const id = msg.id;
    if (typeof id !== "string") return;
    const pendingRef = this.findPendingClientRequest(conn, id);
    if (!pendingRef) return;
    const pendingConn = pendingRef.conn;
    const pending = pendingRef.req;
    if (pendingConn !== conn && !conn.ownsSession(pending.sessionId)) {
      writeStderrLine(
        `qwen serve: /acp permission vote dropped: responding connection ${logSafe(
          conn.connectionId.slice(0, 8)
        )} does not own session ${logSafe(pending.sessionId)} (requestId ${logSafe(
          pending.bridgeRequestId
        )})`
      );
      return;
    }
    try {
      const vote = "error" in msg ? { outcome: { outcome: "cancelled" } } : parsePermissionResponse(
        isObject(msg.result) ? msg.result : {}
      );
      this.bridge.respondToSessionPermission(
        pending.sessionId,
        pending.bridgeRequestId,
        vote,
        this.sessionCtx(conn, pending.sessionId, fromLoopback)
      );
      this.dropResolvedPermission(pendingConn, id);
    } catch (err) {
      writeStderrLine(
        `qwen serve: /acp permission vote failed (${logSafe(pending.sessionId)}): ${logSafe(errMsg(err))}`
      );
      const cancelled = this.cancelAbandonedPermission(
        pending,
        pendingConn.sessions.get(pending.sessionId)?.clientId
      );
      if (cancelled) this.dropResolvedPermission(pendingConn, id);
    }
  }
  async handlePrompt(conn, sessionId, id, params, fromLoopback) {
    const binding = conn.getOrCreateSession(sessionId);
    binding.promptAbort?.abort();
    const abort = new AbortController();
    binding.promptAbort = abort;
    try {
      const result = await this.bridge.sendPrompt(
        sessionId,
        // SECURITY NOTE: `params.sessionId` already equals the routing
        // `sessionId` (both from the same params), so there's no routing
        // divergence today. If the bridge ever trusts an additional
        // `sendPrompt` field by name (e.g. a priority/temperature override),
        // force-stamp it here like the REST surface does (`{ ...body,
        // sessionId, prompt }`) so it can't become client-controlled.
        params,
        abort.signal,
        this.sessionCtx(conn, sessionId, fromLoopback)
      );
      if (id !== void 0) this.replySession(conn, sessionId, id, result);
    } catch (err) {
      const { code, message, data } = toRpcError(err);
      if (id !== void 0) {
        this.replySession(
          conn,
          sessionId,
          id,
          void 0,
          error(id, code, message, data)
        );
      } else {
        writeStderrLine(
          `qwen serve: /acp prompt error (${logSafe(sessionId)}, notification): ${logSafe(errMsg(err))}`
        );
      }
    } finally {
      if (binding.promptAbort === abort) binding.promptAbort = void 0;
    }
  }
  replyConn(conn, id, result) {
    if (id === void 0) return;
    conn.sendConn(success(id, result));
  }
  replySession(conn, sessionId, id, result, errorFrame) {
    if (id === void 0) return;
    const frame = errorFrame ?? success(id, result);
    if (conn.sessions.has(sessionId)) {
      let anchorId;
      try {
        anchorId = this.bridge.getSessionLastEventId(sessionId);
      } catch (err) {
        anchorId = void 0;
        writeStderrLine(
          `qwen serve: /acp replySession(${logSafe(sessionId)}) anchor unavailable, deferring unanchored: ` + logSafe(err instanceof Error ? err.message : String(err))
        );
      }
      conn.sendSessionReply(sessionId, frame, anchorId);
    } else {
      writeStderrLine(
        `qwen serve: /acp replySession(${logSafe(sessionId)}) binding gone mid-flight, reply routed to connection stream ${conn.connectionId.slice(0, 8)}`
      );
      conn.sendConn(frame);
    }
  }
};

// packages/cli/src/serve/acp-http/connection-registry.ts
init_esbuild_shims();
import { randomUUID } from "node:crypto";
var MAX_BUFFERED_FRAMES = 256;
var HARD_BUFFERED_FRAMES_CAP = MAX_BUFFERED_FRAMES * 4;
var DEFAULT_MAX_CONNECTIONS = 64;
var AcpConnection = class {
  constructor(connectionId, fromLoopback, onAbandonPending, onDetachSession) {
    this.onAbandonPending = onAbandonPending;
    this.onDetachSession = onDetachSession;
    this.connectionId = connectionId ?? randomUUID();
    this.clientId = randomUUID();
    this.fromLoopback = fromLoopback;
  }
  static {
    __name(this, "AcpConnection");
  }
  connectionId;
  /** Connection-scoped SSE stream (the client's `GET /acp` with only the conn header). */
  connStream;
  abortController = new AbortController();
  abortSignal = this.abortController.signal;
  /** Frames emitted before the connection stream attached, flushed on attach. */
  connBuffer = [];
  sessions = /* @__PURE__ */ new Map();
  /**
   * Sessions this connection created (`session/new`) or explicitly
   * attached to (`session/load`/`resume`). Per-session operations
   * (subscribe, prompt, cancel, …) are gated on membership here so one
   * connection can't drive or eavesdrop on a session it never claimed.
   */
  ownedSessions = /* @__PURE__ */ new Set();
  /**
   * Sessions with an in-flight `session/close` (between the synchronous
   * ownership-revoke and the bridge close + local teardown). `session/load`
   * / `resume` reject for an id in this set so a close racing a re-load
   * can't have its `finally` teardown destroy the freshly-loaded session.
   */
  closingSessions = /* @__PURE__ */ new Set();
  /** Agent→client requests awaiting a client response, keyed by JSON-RPC id. */
  pending = /* @__PURE__ */ new Map();
  /** Daemon-issued client id reused across this connection's bridge calls. */
  clientId;
  /**
   * True when the `initialize` POST arrived from a kernel-stamped loopback
   * peer. Threaded into per-session bridge contexts so the `local-only`
   * permission policy can gate votes by transport — mirrors the REST
   * surface's `detectFromLoopback(req)`. NOT derived from forgeable
   * headers (`X-Forwarded-For` etc).
   */
  fromLoopback;
  /**
   * Set by `destroy()`. An in-flight `session/new`/`load`/`resume` whose
   * bridge call resolves AFTER teardown checks this to kill/detach the
   * late-registered session, so a `DELETE` (or idle sweep) racing a spawn
   * doesn't orphan a child process / phantom clientId.
   */
  destroyed = false;
  /**
   * Grace-period reap timer armed when the connection-scoped SSE stream
   * closes; cleared on reconnect (`attachConnStream`) or teardown. Avoids a
   * dead connection locking its `ownedSessions` (and counting against
   * `maxConnections`) for the full 30-min idle TTL.
   */
  connGraceTimer;
  /**
   * True once the connection grace timer has fired. The timer is one-shot, so
   * if it fired while a session was still mid-reconnect (`hasRecoverableSession`
   * blocked the reap), nothing would re-check the connection after that session
   * later tore down — it would linger until the 30-min idle sweep. This flag
   * lets `onSessionGraceExpired` (fired when a session grace expires) recognize
   * "the conn grace already elapsed" and run the reap that was deferred.
   */
  connGraceExpired = false;
  /**
   * Set by the transport layer (`index.ts`) when the connection grace timer is
   * armed. Invoked after a session's reclaim grace expires so a connection that
   * was blocked from reaping by a then-recoverable session gets re-evaluated
   * instead of leaking until the idle sweep.
   */
  onSessionGraceExpired;
  lastActiveMs = Date.now();
  idCounter = 0;
  /**
   * Allocate a fresh JSON-RPC id for an agent→client request. STRING-typed
   * (`_qwen_perm_<conn>_N`) so it can never collide with a client-originated id —
   * JSON-RPC 2.0 permits clients to use any number (incl. negatives) or
   * string, so a numeric namespace wasn't actually safe.
   */
  nextId() {
    this.idCounter += 1;
    return `_qwen_perm_${this.connectionId}_${this.idCounter}`;
  }
  touch() {
    this.lastActiveMs = Date.now();
  }
  ownSession(sessionId) {
    this.ownedSessions.add(sessionId);
  }
  ownsSession(sessionId) {
    return this.ownedSessions.has(sessionId);
  }
  getOrCreateSession(sessionId) {
    let binding = this.sessions.get(sessionId);
    if (!binding) {
      binding = { sessionId, abort: new AbortController(), buffer: [] };
      this.sessions.set(sessionId, binding);
    }
    return binding;
  }
  getDiagnostic() {
    const liveStreams = /* @__PURE__ */ new Set();
    if (this.connStream && !this.connStream.isClosed) {
      liveStreams.add(this.connStream);
    }
    let sessionStreams = 0;
    let bufferedSessionFrames = 0;
    for (const binding of this.sessions.values()) {
      bufferedSessionFrames += binding.buffer.length;
      if (binding.stream && !binding.stream.isClosed) {
        sessionStreams += 1;
        liveStreams.add(binding.stream);
      }
    }
    let sseStreams = 0;
    let wsStreams = 0;
    for (const stream of liveStreams) {
      if (stream.kind === "sse") sseStreams += 1;
      if (stream.kind === "ws") wsStreams += 1;
    }
    return {
      connectionIdPrefix: this.connectionId.slice(0, 8),
      fromLoopback: this.fromLoopback,
      destroyed: this.destroyed,
      lastActiveMs: this.lastActiveMs,
      ownedSessionCount: this.ownedSessions.size,
      sessionBindingCount: this.sessions.size,
      closingSessionCount: this.closingSessions.size,
      pendingClientRequests: this.pending.size,
      connectionStreamOpen: this.connStream !== void 0 && !this.connStream.isClosed,
      sessionStreams,
      sseStreams,
      wsStreams,
      bufferedConnectionFrames: this.connBuffer.length,
      bufferedSessionFrames
    };
  }
  /** Send a frame on the connection-scoped stream (buffer until it attaches). */
  sendConn(frame) {
    if (this.connStream && !this.connStream.isClosed) {
      void this.connStream.send(frame);
    } else {
      pushCapped(this.connBuffer, frame, `conn ${this.connectionId}`);
    }
  }
  /** True if any session currently has a live (open) SSE stream. */
  hasLiveSessionStream() {
    for (const b of this.sessions.values()) {
      if (b.stream && !b.stream.isClosed) return true;
    }
    return false;
  }
  /**
   * True if any session is mid-reconnect: its transport stream detached but
   * its `SESSION_GRACE_MS` reclaim window is still armed (`graceTimer` set),
   * so the binding — ownership + in-flight prompt — is being held open for an
   * imminent resume. The connection reaper must count these as activity:
   * otherwise, when the connection-scoped stream closes first and a session
   * stream detaches just after, the connection grace timer could delete the
   * whole connection (and `destroy()` abort the prompt) while the session is
   * still inside its OWN grace window — the reconnect would then 404.
   */
  hasRecoverableSession() {
    for (const b of this.sessions.values()) {
      if (b.graceTimer) return true;
    }
    return false;
  }
  /** Cancel a pending grace-period reap (e.g. on conn-stream reconnect). */
  clearGraceTimer() {
    if (this.connGraceTimer) {
      clearTimeout(this.connGraceTimer);
      this.connGraceTimer = void 0;
    }
    this.connGraceExpired = false;
  }
  /** Attach the connection-scoped stream and flush any buffered frames. */
  attachConnStream(stream) {
    this.clearGraceTimer();
    if (this.connStream && this.connStream !== stream) this.connStream.close();
    this.connStream = stream;
    for (const frame of this.connBuffer.splice(0)) void stream.send(frame);
  }
  /**
   * Send a frame on a session-scoped stream (buffer until it attaches).
   * LOOKUP-ONLY: drops the frame when the session has no binding — a binding
   * always exists for a live session (created at `session/new`/`load`/
   * `resume`), so a missing one means the session was torn down. Auto-
   * creating here would resurrect a ghost binding (no stream, no owner) that
   * buffers up to 256 late pump/reply frames forever.
   */
  sendSession(sessionId, frame, id) {
    const binding = this.sessions.get(sessionId);
    if (!binding) return;
    if (binding.stream && !binding.stream.isClosed) {
      void binding.stream.send(frame, id);
    } else {
      pushCapped(
        binding.buffer,
        { frame, id },
        `session ${sessionId}`,
        (e) => e.id
      );
    }
  }
  /**
   * Send an OUT-OF-BAND session JSON-RPC reply (a `session/prompt` result and
   * friends from `replySession`) — id-less on the wire (no ring cursor).
   *
   * Unlike `sendSession`, this defers the reply behind a watermark while a ring
   * replay is catching up. `anchorId` is the bus head id at the moment the reply
   * was produced (every content event that should precede the reply has id ≤
   * `anchorId`). The reply is held until the pump has DELIVERED through that id
   * (`releaseDeferredSessionReplies`), so a prompt that finishes during a slow
   * replay can't land ahead of tail content still queued behind `replay_complete`
   * (§1.8 W1 — the truncated-body reordering).
   *
   * It defers when EITHER a resume replay is in flight (`replayPending`) OR the
   * stream is detached OR replies are already queued ahead of it (`buffer` not
   * empty) — that last case keeps a just-produced reply from overtaking an
   * earlier one still waiting on its watermark. Once the buffer drains and replay
   * is done, replies go straight to the wire (steady state, unchanged from a
   * non-resumed stream).
   */
  sendSessionReply(sessionId, frame, anchorId) {
    const binding = this.sessions.get(sessionId);
    if (!binding) return;
    if (binding.stream && !binding.stream.isClosed && !binding.replayPending && binding.buffer.length === 0) {
      void binding.stream.send(frame);
      return;
    }
    pushCapped(
      binding.buffer,
      { frame, id: void 0, anchorId },
      `session ${sessionId}`,
      (e) => e.id
    );
  }
  /**
   * Release deferred out-of-band replies (`sendSessionReply`) whose watermark
   * (`anchorId`) the pump has now passed: every leading id-less buffer entry
   * with `anchorId ≤ deliveredId` is flushed to the live stream, in order. The
   * pump calls this after delivering each content event (and at `replay_complete`
   * with the last replayed id), so each reply lands immediately AFTER the last
   * content event that preceded it — never ahead of tail content still queued.
   *
   * Stops at the first entry that is id-bearing (not a deferred reply) or whose
   * anchor is still ahead of `deliveredId`, preserving stream order. No-op if the
   * stream isn't live (the entries stay buffered for the next attach).
   */
  releaseDeferredSessionReplies(sessionId, deliveredId) {
    const binding = this.sessions.get(sessionId);
    if (!binding || !binding.stream || binding.stream.isClosed) return;
    while (binding.buffer.length > 0) {
      const front = binding.buffer[0];
      if (front.id !== void 0) break;
      if (front.anchorId === void 0) {
        if (binding.replayPending) break;
      } else if (front.anchorId > deliveredId) {
        break;
      }
      binding.buffer.shift();
      void binding.stream.send(front.frame);
    }
  }
  /**
   * Attach a session-scoped stream: close any prior stream, abort the prior
   * subscription, install the caller's FRESH AbortController (the old one is
   * aborted and can never resume — reusing it would leave the new stream
   * event-starved), flush buffered frames, and return the binding.
   */
  attachSessionStream(sessionId, stream, abort, resumeFromEventId) {
    const binding = this.getOrCreateSession(sessionId);
    if (binding.graceTimer) {
      clearTimeout(binding.graceTimer);
      binding.graceTimer = void 0;
      writeStderrLine(
        `qwen serve: /acp session reclaimed within grace (${logSafe(sessionId)})`
      );
    }
    const prevStream = binding.stream;
    binding.abort.abort();
    binding.abort = abort;
    binding.stream = stream;
    if (prevStream && prevStream !== stream && prevStream !== this.connStream) {
      prevStream.close();
    }
    binding.replayPending = resumeFromEventId !== void 0;
    if (binding.replayPending) {
      writeStderrLine(
        `qwen serve: /acp replay deferral armed (${logSafe(sessionId)}, from id ${resumeFromEventId})`
      );
    }
    const gap = binding.buffer.splice(0);
    for (const entry of gap) {
      if (resumeFromEventId === void 0) {
        void stream.send(entry.frame, entry.id);
      } else if (entry.id !== void 0) {
        continue;
      } else {
        binding.buffer.push(entry);
      }
    }
    return binding;
  }
  /**
   * The ring replay drained (`replay_complete`): stop deferring NEW replies that
   * are anchored within the replayed range, and release every buffered reply
   * whose watermark the replay already passed (`anchorId ≤ lastReplayedId`).
   *
   * Replies anchored ABOVE the replayed range stay buffered — their content
   * hasn't been delivered yet because the turn was still running at reconnect
   * (the content flows as LIVE events after `replay_complete`). The per-event
   * `releaseDeferredSessionReplies` calls drain those as the matching live
   * content arrives, so a result produced during a slow replay still lands after
   * its tail content (§1.8 W1), not at the boundary.
   */
  endReplayDeferral(sessionId, lastReplayedId, evictionOccurred = false) {
    const binding = this.sessions.get(sessionId);
    if (!binding) return;
    binding.replayPending = false;
    if (evictionOccurred) {
      this.flushBufferedSessionFrames(sessionId);
      return;
    }
    this.releaseDeferredSessionReplies(sessionId, lastReplayedId);
  }
  /**
   * Final, UNCONDITIONAL flush of everything still buffered — used when the pump
   * ends with no more events coming (clean iterator end / live-only subscription
   * with no replay boundary). Releases any remaining deferred replies regardless
   * of watermark: their anchored content will never arrive, so holding them
   * would strand the result forever.
   *
   * No-op if the session has no live stream (frames stay buffered for the next
   * attach) — but `replayPending` is cleared regardless, since no replay is in
   * flight once the pump has settled.
   *
   * The frames are enqueued synchronously, in buffer order: `SseStream`
   * serializes every `send` through one `writeChain`, so wire order is fixed by
   * call order here. We deliberately do NOT `await` each `send` between `shift`s
   * — doing so would open a window where a live event arriving mid-drain enqueues
   * BETWEEN two deferred frames, reordering the very replies this deferral exists
   * to keep in order (§1.8 W1).
   */
  flushBufferedSessionFrames(sessionId) {
    const binding = this.sessions.get(sessionId);
    if (!binding) return;
    binding.replayPending = false;
    if (!binding.stream || binding.stream.isClosed || binding.buffer.length === 0)
      return;
    for (const { frame, id } of binding.buffer.splice(0)) {
      void binding.stream.send(frame, id);
    }
  }
  /**
   * Transport-level session-stream close (proxy idle-close / network blip) —
   * as opposed to an explicit `session/close`. Detaches ONLY the stream and
   * its event subscription while KEEPING the binding, ownership, the in-flight
   * prompt, and the bridge-client registration, so a reconnect within
   * `graceMs` can resume (ring replay backfills the gap — §1.8). If no
   * reconnect arrives, the grace timer runs the full `closeSessionStream`
   * teardown, bounding the runaway-prompt cost. Identity-guarded: a stale
   * stream's close can't detach a newer reconnect's stream.
   */
  detachSessionStream(sessionId, stream, graceMs) {
    const binding = this.sessions.get(sessionId);
    if (!binding || binding.stream !== stream) return;
    writeStderrLine(
      `qwen serve: /acp session stream detached (${logSafe(sessionId)}), grace=${graceMs}ms`
    );
    binding.abort.abort();
    binding.stream = void 0;
    if (binding.graceTimer) clearTimeout(binding.graceTimer);
    binding.graceTimer = setTimeout(() => {
      writeStderrLine(
        `qwen serve: /acp session grace expired (${logSafe(sessionId)}), no reconnect within ${graceMs}ms \u2014 tearing down`
      );
      try {
        this.closeSessionStream(sessionId);
      } catch (err) {
        writeStderrLine(
          `qwen serve: /acp teardown failed during grace expiry (${logSafe(sessionId)}): ` + (err instanceof Error ? err.message : String(err))
        );
      }
      try {
        this.onSessionGraceExpired?.();
      } catch (err) {
        writeStderrLine(
          `qwen serve: /acp onSessionGraceExpired failed during grace expiry (${logSafe(sessionId)}): ` + (err instanceof Error ? err.message : String(err))
        );
      }
    }, graceMs);
    binding.graceTimer.unref?.();
  }
  closeSessionStream(sessionId) {
    const binding = this.sessions.get(sessionId);
    if (!binding) return;
    this.teardownBinding(binding);
    this.sessions.delete(sessionId);
    this.ownedSessions.delete(sessionId);
  }
  destroy() {
    this.destroyed = true;
    this.abortController.abort();
    this.clearGraceTimer();
    for (const binding of this.sessions.values()) {
      try {
        this.teardownBinding(binding);
      } catch (err) {
        writeStderrLine(
          `qwen serve: /acp teardownBinding(${logSafe(binding.sessionId)}) failed during destroy: ${err instanceof Error ? err.message : String(err)}`
        );
      }
    }
    this.sessions.clear();
    this.ownedSessions.clear();
    this.pending.clear();
    this.connStream?.close();
  }
  teardownBinding(binding) {
    if (binding.graceTimer) {
      clearTimeout(binding.graceTimer);
      binding.graceTimer = void 0;
    }
    binding.abort.abort();
    binding.promptAbort?.abort();
    if (binding.stream && binding.stream !== this.connStream) {
      binding.stream.close();
    }
    this.abandonPendingForSession(binding.sessionId, binding.clientId);
    this.onDetachSession?.(binding.sessionId, binding.clientId);
  }
  /**
   * Cancel + drop any pending agent→client requests for a closing session.
   * This is the LAST-RESORT recovery path: `resolveClientResponse` retains a
   * pending entry on double-failure (vote AND cancel both threw) precisely so
   * this teardown sweep can retry the cancel. We always drop the entry here
   * (the connection is going away — there is no further retry after teardown),
   * but if the cancel itself still fails (triple-failure) the bridge mediator
   * may be stuck awaiting a vote that will never arrive, so log it for the
   * operator rather than failing silently.
   */
  abandonPendingForSession(sessionId, clientId) {
    for (const [id, req] of this.pending) {
      if (req.sessionId !== sessionId) continue;
      this.pending.delete(id);
      const cancelled = this.onAbandonPending?.(req, clientId) ?? true;
      if (!cancelled) {
        writeStderrLine(
          `qwen serve: /acp MEDIATOR STUCK: abandonPendingForSession(${logSafe(sessionId)}) cancel failed for ${logSafe(req.bridgeRequestId)}`
        );
      }
    }
  }
};
function pushCapped(buf, frame, label = "stream", getId) {
  if (buf.length >= MAX_BUFFERED_FRAMES) {
    const replayable = getId ? buf.findIndex((e) => getId(e) !== void 0) : 0;
    if (replayable === -1) {
      if (buf.length >= HARD_BUFFERED_FRAMES_CAP) {
        buf.shift();
        writeStderrLine(
          `qwen serve: /acp HARD buffer cap breached (${label}) \u2014 dropping oldest id-less reply (its caller may hang); buffer was ${buf.length + 1}`
        );
      } else if (buf.length === MAX_BUFFERED_FRAMES) {
        writeStderrLine(
          `qwen serve: /acp pre-attach buffer over soft cap (${label}) \u2014 id-less replies are irreplaceable, not dropping`
        );
      }
    } else {
      const [dropped] = buf.splice(replayable, 1);
      const droppedId = getId?.(dropped);
      writeStderrLine(
        `qwen serve: /acp pre-attach buffer full (${label}), dropped frame` + (droppedId !== void 0 ? ` id ${droppedId}` : "")
      );
    }
  }
  buf.push(frame);
}
__name(pushCapped, "pushCapped");
var ConnectionRegistry = class {
  constructor(onAbandonPending, onDetachSession, maxConnections = DEFAULT_MAX_CONNECTIONS, idleTtlMs = 30 * 6e4) {
    this.onAbandonPending = onAbandonPending;
    this.onDetachSession = onDetachSession;
    this.maxConnections = maxConnections;
    this.idleTtlMs = idleTtlMs;
    this.sweepTimer = setInterval(() => this.sweep(), 6e4);
    this.sweepTimer.unref();
  }
  static {
    __name(this, "ConnectionRegistry");
  }
  byId = /* @__PURE__ */ new Map();
  sweepTimer;
  /**
   * Mint a connection, or return `undefined` when the live-connection cap
   * is reached (the caller answers `503`). Bounds an `initialize` flood from
   * growing the registry without limit through the full TTL window.
   */
  create(fromLoopback) {
    if (this.maxConnections > 0 && this.byId.size >= this.maxConnections) {
      return void 0;
    }
    const conn = new AcpConnection(
      void 0,
      fromLoopback,
      this.onAbandonPending,
      this.onDetachSession
    );
    this.byId.set(conn.connectionId, conn);
    return conn;
  }
  get(connectionId) {
    if (!connectionId) return void 0;
    const conn = this.byId.get(connectionId);
    conn?.touch();
    return conn;
  }
  findPendingClientRequest(id) {
    if (id.startsWith("_qwen_perm_")) {
      const body = id.slice("_qwen_perm_".length);
      const lastUnderscore = body.lastIndexOf("_");
      if (lastUnderscore > 0) {
        const conn = this.byId.get(body.slice(0, lastUnderscore));
        const req = conn?.pending.get(id);
        if (conn && req) return { conn, id, req };
      }
    }
    for (const conn of this.byId.values()) {
      const req = conn.pending.get(id);
      if (req) return { conn, id, req };
    }
    return void 0;
  }
  /**
   * Locate a pending permission entry matching `requestId` (a bridge
   * `bridgeRequestId`, i.e. a per-request `randomUUID()`) and optionally
   * `sessionId`, returning the first match.
   *
   * NOTE: `requestId` is unique per *request*, not per *pending entry*. The
   * per-entry unique id is the `conn.pending` map key
   * (`_qwen_perm_<connectionId>_N`), which is NOT what is matched here. A
   * `permission_request` is delivered to every live subscriber of its session,
   * so when connections co-own a session (multi-client attach) each mints its
   * own entry sharing the same `bridgeRequestId`. More than one entry can
   * therefore match, so this is a *read-only locator* for deriving a session /
   * ownership from a wire `requestId`. To DELETE a resolved entry, act on the
   * specific `conn`/map-key the caller already holds (see
   * `AcpDispatcher.dropOwnPendingPermission`) — never delete by re-matching
   * here, which could hit a sibling co-owner's entry.
   */
  findPendingPermission(requestId, sessionId) {
    for (const conn of this.byId.values()) {
      for (const [id, req] of conn.pending) {
        if (req.kind === "permission" && req.bridgeRequestId === requestId && (sessionId === void 0 || req.sessionId === sessionId)) {
          return { conn, id, req };
        }
      }
    }
    return void 0;
  }
  delete(connectionId) {
    const conn = this.byId.get(connectionId);
    if (!conn) return false;
    conn.destroy();
    return this.byId.delete(connectionId);
  }
  get size() {
    return this.byId.size;
  }
  /** The configured concurrent-connection cap (for operator-facing logs). */
  get connectionCap() {
    return this.maxConnections;
  }
  getSnapshot() {
    const connections = [...this.byId.values()].map(
      (conn) => conn.getDiagnostic()
    );
    return {
      connectionCount: this.byId.size,
      connectionCap: this.maxConnections > 0 && Number.isFinite(this.maxConnections) ? this.maxConnections : null,
      connectionStreams: connections.filter((conn) => conn.connectionStreamOpen).length,
      sessionStreams: sumBy(connections, (conn) => conn.sessionStreams),
      sseStreams: sumBy(connections, (conn) => conn.sseStreams),
      wsStreams: sumBy(connections, (conn) => conn.wsStreams),
      pendingClientRequests: sumBy(
        connections,
        (conn) => conn.pendingClientRequests
      ),
      connections
    };
  }
  dispose() {
    clearInterval(this.sweepTimer);
    for (const id of [...this.byId.keys()]) this.delete(id);
  }
  sweep() {
    const cutoff = Date.now() - this.idleTtlMs;
    for (const [id, conn] of this.byId) {
      if (conn.lastActiveMs >= cutoff) continue;
      writeStderrLine(
        `qwen serve: /acp reaping idle connection ${id} (idle > ${Math.round(this.idleTtlMs / 6e4)}m, ${conn.sessions.size} session(s))`
      );
      this.delete(id);
    }
  }
};
function sumBy(values, select) {
  let total = 0;
  for (const value of values) total += select(value);
  return total;
}
__name(sumBy, "sumBy");

// packages/cli/src/serve/acp-http/sse-stream.ts
init_esbuild_shims();
var SseStream = class {
  constructor(res, onClose, onHeartbeat) {
    this.res = res;
    this.onClose = onClose;
    this.onHeartbeat = onHeartbeat;
  }
  static {
    __name(this, "SseStream");
  }
  kind = "sse";
  writeChain = Promise.resolve();
  heartbeat;
  closed = false;
  cleanupFn;
  /** Write SSE headers + retry hint and start the heartbeat. */
  open() {
    this.res.status(200);
    this.res.setHeader("Content-Type", "text/event-stream");
    this.res.setHeader("Cache-Control", "no-cache, no-transform");
    this.res.setHeader("Connection", "keep-alive");
    this.res.setHeader("X-Accel-Buffering", "no");
    this.res.flushHeaders();
    void this.writeRaw("retry: 3000\n\n");
    this.heartbeat = setInterval(() => {
      if (this.closed) return;
      this.onHeartbeat?.();
      void this.writeRaw(": hb\n\n");
    }, 15e3);
    this.heartbeat.unref();
    this.cleanupFn = () => this.close();
    this.res.req.on("close", this.cleanupFn);
    this.res.on("error", this.cleanupFn);
  }
  /**
   * Serialize a JSON-RPC message as one SSE frame. When `id` is supplied
   * (a bus event id) prepend an `id:` line so an EventSource/SSE client
   * tracks it and resends it as `Last-Event-ID` on reconnect — the resume
   * cursor for ring replay. Omitted for JSON-RPC responses and synthetic
   * terminal frames (no bus id), matching REST `formatSseFrame`.
   */
  send(message, id) {
    const idLine = id !== void 0 ? `id: ${id}
` : "";
    return this.writeRaw(`${idLine}data: ${JSON.stringify(message)}

`);
  }
  get isClosed() {
    return this.closed;
  }
  close() {
    if (this.closed) return;
    this.closed = true;
    if (this.heartbeat) clearInterval(this.heartbeat);
    if (this.cleanupFn) {
      this.res.req.off("close", this.cleanupFn);
      this.res.off("error", this.cleanupFn);
      this.cleanupFn = void 0;
    }
    try {
      if (!this.res.writableEnded) this.res.end();
    } catch {
    }
    try {
      this.onClose?.();
    } catch (err) {
      writeStderrLine(
        `qwen serve: /acp SSE onClose threw: ${err instanceof Error ? err.message : String(err)}`
      );
    }
  }
  writeRaw(chunk) {
    const next = this.writeChain.then(() => this.doWrite(chunk));
    this.writeChain = next.catch((err) => {
      if (!this.closed) {
        writeStderrLine(
          `qwen serve: /acp SSE write failed, closing stream: ${err instanceof Error ? err.message : String(err)}`
        );
        this.close();
      }
      return void 0;
    });
    return next;
  }
  doWrite(chunk) {
    return new Promise((resolve5, reject) => {
      if (this.closed || this.res.writableEnded) {
        resolve5();
        return;
      }
      let ok;
      try {
        ok = this.res.write(chunk);
      } catch (err) {
        reject(err);
        return;
      }
      if (ok) {
        resolve5();
        return;
      }
      const cleanup = /* @__PURE__ */ __name(() => {
        this.res.off("drain", onDrain);
        this.res.off("close", onCloseEv);
        this.res.off("error", onErrorEv);
      }, "cleanup");
      const onDrain = /* @__PURE__ */ __name(() => {
        cleanup();
        resolve5();
      }, "onDrain");
      const onCloseEv = /* @__PURE__ */ __name(() => {
        cleanup();
        resolve5();
      }, "onCloseEv");
      const onErrorEv = /* @__PURE__ */ __name((err) => {
        cleanup();
        reject(err);
      }, "onErrorEv");
      this.res.once("drain", onDrain);
      this.res.once("close", onCloseEv);
      this.res.once("error", onErrorEv);
    });
  }
};

// packages/cli/src/serve/acp-http/ws-stream.ts
init_esbuild_shims();
var WsStream = class {
  constructor(ws, onClose, onHeartbeat) {
    this.ws = ws;
    this.onClose = onClose;
    this.onHeartbeat = onHeartbeat;
    ws.on("close", () => this.close());
    ws.on("error", (err) => {
      writeStderrLine(
        `qwen serve: /acp WS error: ${err instanceof Error ? err.message : String(err)}`
      );
      this.close();
    });
    let alive = true;
    ws.on("pong", () => {
      alive = true;
    });
    this.heartbeat = setInterval(() => {
      if (this._closed) return;
      if (!alive) {
        this.close();
        return;
      }
      alive = false;
      try {
        this.onHeartbeat?.();
      } catch {
      }
      try {
        this.ws.ping();
      } catch {
      }
    }, 15e3);
    this.heartbeat.unref();
  }
  static {
    __name(this, "WsStream");
  }
  kind = "ws";
  writeChain = Promise.resolve();
  _closed = false;
  heartbeat;
  // `_id` (bus event id) is accepted for `TransportStream` parity but ignored:
  // WebSocket is a stateful connection with no SSE `Last-Event-ID` replay
  // (matches `AcpWsTransport.supportsReplay = false`).
  send(message, _id) {
    const data = JSON.stringify(message);
    const next = this.writeChain.then(
      () => new Promise((resolve5, reject) => {
        if (this._closed) {
          resolve5();
          return;
        }
        this.ws.send(data, (err) => {
          if (err) reject(err);
          else resolve5();
        });
      })
    );
    this.writeChain = next.catch((err) => {
      if (!this._closed) {
        writeStderrLine(
          `qwen serve: /acp WS write failed: ${err instanceof Error ? err.message : String(err)}`
        );
        this.close();
      }
    });
    return this.writeChain;
  }
  get isClosed() {
    return this._closed;
  }
  close() {
    if (this._closed) return;
    this._closed = true;
    if (this.heartbeat) clearInterval(this.heartbeat);
    try {
      if (this.ws.readyState === this.ws.OPEN) this.ws.close(1e3);
    } catch {
    }
    try {
      this.onClose?.();
    } catch (err) {
      writeStderrLine(
        `qwen serve: /acp WS onClose threw: ${err instanceof Error ? err.message : String(err)}`
      );
    }
  }
};

// packages/cli/src/serve/sse-last-event-id.ts
init_esbuild_shims();
function safeLogValue2(raw) {
  const s = typeof raw === "string" ? raw : String(raw);
  const clipped = s.length > 64 ? `${s.slice(0, 64)}\u2026` : s;
  return logSafe(clipped);
}
__name(safeLogValue2, "safeLogValue");
function parseLastEventId2(raw, logPrefix = "") {
  if (typeof raw !== "string" || !/^\d+$/.test(raw)) {
    if (typeof raw === "string" && raw.length > 0) {
      writeStderrLine(
        `qwen serve: ${logPrefix}rejected Last-Event-ID ${safeLogValue2(raw)} (not a decimal integer)`
      );
    }
    return void 0;
  }
  const n = Number.parseInt(raw, 10);
  if (!Number.isFinite(n) || n > Number.MAX_SAFE_INTEGER) {
    writeStderrLine(
      `qwen serve: ${logPrefix}rejected Last-Event-ID ${safeLogValue2(raw)} (exceeds Number.MAX_SAFE_INTEGER)`
    );
    return void 0;
  }
  return n;
}
__name(parseLastEventId2, "parseLastEventId");

// packages/cli/src/serve/acp-http/client-mcp-ws.ts
init_esbuild_shims();
var CLIENT_MCP_FRAME_TYPES = {
  register: "mcp_register",
  message: "mcp_message",
  unregister: "mcp_unregister"
};
var MAX_SERVERS_PER_CONNECTION = 10;
var ClientMcpWsConnection = class {
  constructor(sendFrame, provider) {
    this.sendFrame = sendFrame;
    this.provider = provider;
    this.registrar = new ClientMcpRegistrar({
      sendFrame: /* @__PURE__ */ __name((frame) => {
        this.sendFrame({
          type: CLIENT_MCP_FRAME_TYPES.message,
          id: frame.id,
          server: frame.server,
          payload: frame.payload
        });
      }, "sendFrame")
    });
  }
  static {
    __name(this, "ClientMcpWsConnection");
  }
  registrar;
  disposed = false;
  /**
   * Route a parsed inbound frame. Returns a structured result the WS layer can
   * turn into an ack/error reply (or ignore). Never throws — protocol errors
   * are returned as `{ kind: 'error' }`.
   */
  async handleFrame(frame) {
    if (this.disposed) {
      return { kind: "error", code: "closed", message: "connection closed" };
    }
    switch (frame.type) {
      case CLIENT_MCP_FRAME_TYPES.register:
        return this.handleRegister(frame.server);
      case CLIENT_MCP_FRAME_TYPES.unregister:
        return this.handleUnregister(frame.server);
      case CLIENT_MCP_FRAME_TYPES.message:
        return this.handleMessage(frame.id, frame.payload);
      default:
        return {
          kind: "ignored",
          reason: `unknown client-mcp frame type: ${String(frame.type)}`
        };
    }
  }
  /** Whether a frame's `type` is one this module owns. */
  static isClientMcpFrameType(type) {
    return type === CLIENT_MCP_FRAME_TYPES.register || type === CLIENT_MCP_FRAME_TYPES.message || type === CLIENT_MCP_FRAME_TYPES.unregister;
  }
  async handleRegister(server) {
    if (!isValidServerName(server)) {
      return {
        kind: "error",
        code: "invalid_server_name",
        message: "server must be \u2264256 chars, alphanumeric + underscore/hyphen, and not a reserved JS property name"
      };
    }
    if (this.registrar.hasServer(server)) {
      return {
        kind: "error",
        code: "already_registered",
        message: `server '${server}' is already registered on this connection`
      };
    }
    if (this.registrar.serverCount() >= MAX_SERVERS_PER_CONNECTION) {
      return {
        kind: "error",
        code: "too_many_servers",
        message: `connection has reached the maximum of ${MAX_SERVERS_PER_CONNECTION} registered MCP servers`
      };
    }
    if (!this.provider) {
      return {
        kind: "error",
        code: "not_wired",
        message: "client_mcp_over_ws is advertised but no McpClientManager provider is wired into this daemon process"
      };
    }
    this.registrar.registerServer(server);
    try {
      const { toolCount } = await this.provider.registerClientMcpServer(
        server,
        this.registrar.sendSdkMcpMessage
      );
      if (this.disposed) {
        this.registrar.unregisterServer(server);
        await this.provider.unregisterClientMcpServer(server);
        return {
          kind: "error",
          code: "closed",
          message: "connection disposed during register"
        };
      }
      return { kind: "registered", server, toolCount };
    } catch (err) {
      this.registrar.unregisterServer(server);
      return {
        kind: "error",
        code: "register_failed",
        message: err instanceof Error ? err.message : String(err)
      };
    }
  }
  async handleUnregister(server) {
    if (this.disposed) {
      return { kind: "unregistered", server: String(server) };
    }
    if (!isValidServerName(server)) {
      return {
        kind: "error",
        code: "invalid_server_name",
        message: "server name is invalid"
      };
    }
    const existed = this.registrar.unregisterServer(server);
    if (existed && this.provider) {
      try {
        await this.provider.unregisterClientMcpServer(server);
      } catch {
      }
    }
    return { kind: "unregistered", server };
  }
  handleMessage(id, payload) {
    if (typeof id !== "string" || id.length === 0) {
      return {
        kind: "error",
        code: "invalid_id",
        message: "`id` must be a non-empty string"
      };
    }
    if (payload === null || typeof payload !== "object") {
      return {
        kind: "error",
        code: "invalid_payload",
        message: "`payload` must be a JSON-RPC message object"
      };
    }
    const resolved = this.registrar.resolveMessage(
      id,
      payload
    );
    return resolved ? { kind: "message_resolved", id } : { kind: "ignored", reason: `no pending request for id '${id}'` };
  }
  /** Currently-registered server names on this connection. */
  registeredServers() {
    return this.registrar.registeredServers();
  }
  /** In-flight `mcp_message` round-trip count (for tests / accounting). */
  pendingCount() {
    return this.registrar.pendingCount();
  }
  /**
   * Tear the connection down: reject pending, forget servers, and best-effort
   * remove each from the provider. Idempotent.
   */
  async dispose(reason = "client MCP WS connection closed") {
    if (this.disposed) return;
    this.disposed = true;
    const servers = this.registrar.registeredServers();
    this.registrar.close(reason);
    if (this.provider) {
      await Promise.allSettled(
        servers.map(
          (server) => this.provider.unregisterClientMcpServer(server)
        )
      );
    }
  }
};

// packages/cli/src/serve/cdp-tunnel/cdp-reverse-link.ts
init_esbuild_shims();
var CDP_FRAME_TYPES = {
  /** Ask the extension to `chrome.debugger.attach` the active tab. */
  attach: "cdp_attach",
  /** Ack from the extension that the tab is attached. */
  attached: "cdp_attached",
  /** A page-domain CDP command to run on the real tab. */
  command: "cdp_command",
  /** The result (or error) of a `cdp_command`, correlated by `id`. */
  result: "cdp_result",
  /** A CDP event emitted by the real tab. */
  event: "cdp_event",
  /** The tab/debugger detached (user opened DevTools, page crashed, …). */
  detach: "cdp_detach",
  /**
   * Tell the extension to release its `chrome.debugger` attachment because the
   * `/cdp` puppeteer client went away (the extension is still connected).
   */
  release: "cdp_release"
};
var DEFAULT_COMMAND_TIMEOUT_MS = 17e4;
var COMMAND_PROGRESS_LOG_MS = 3e4;
function isCdpInboundFrameType(type) {
  return type === CDP_FRAME_TYPES.result || type === CDP_FRAME_TYPES.event || type === CDP_FRAME_TYPES.attached || type === CDP_FRAME_TYPES.detach;
}
__name(isCdpInboundFrameType, "isCdpInboundFrameType");
function isCdpInboundFrame(frame) {
  return isCdpInboundFrameType(frame.type);
}
__name(isCdpInboundFrame, "isCdpInboundFrame");
var CdpReverseLink = class {
  constructor(sendToExtension, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, log) {
    this.sendToExtension = sendToExtension;
    this.commandTimeoutMs = commandTimeoutMs;
    this.log = log;
  }
  static {
    __name(this, "CdpReverseLink");
  }
  emulator;
  nextId = 1;
  pending = /* @__PURE__ */ new Map();
  disposed = false;
  attached = false;
  attachPromise;
  /** Resolver for the in-flight `cdp_attach` (if any). */
  pendingAttach;
  /** Called when the extension reports the tab detached. */
  onDetach;
  /** Called when lazy `cdp_attach` fails before any page command can run. */
  onAttachFailure;
  /** Wire the emulator whose `forwardToTab` this link backs. */
  bindEmulator(emulator) {
    this.emulator = emulator;
  }
  /**
   * The {@link CdpEmulatorCallbacks.forwardToTab} implementation: send a
   * `cdp_command` to the extension and await the correlated `cdp_result`.
   */
  forwardToTab = /* @__PURE__ */ __name(async (method, params) => {
    if (this.disposed) {
      throw { code: -32e3, message: "CDP tunnel closed" };
    }
    await this.attach();
    return new Promise((resolve5, reject) => {
      if (this.disposed) {
        reject({ code: -32e3, message: "CDP tunnel closed" });
        return;
      }
      const id = this.nextId++;
      const timer = this.armTimeout(
        id,
        `CDP command id=${id} method=${method} timed out after ${this.commandTimeoutMs}ms`
      );
      const progressTimer = this.armProgressLog(id, method);
      this.pending.set(id, { resolve: resolve5, reject, timer, progressTimer });
      try {
        this.log?.(
          `qwen serve: /cdp forwarded command id=${id} method=${method} to extension`
        );
        this.sendToExtension({
          type: CDP_FRAME_TYPES.command,
          id,
          method,
          params
        });
      } catch (err) {
        this.settleReject(id, {
          code: -32e3,
          message: err instanceof Error ? err.message : String(err)
        });
      }
    });
  }, "forwardToTab");
  /**
   * Ask the extension to attach `chrome.debugger` to the active tab. Resolves
   * once the extension acks `cdp_attached` (or rejects on error/timeout).
   */
  attach() {
    if (this.attached) return Promise.resolve({});
    if (this.attachPromise) return this.attachPromise;
    this.attachPromise = new Promise(
      (resolve5, reject) => {
        if (this.disposed) {
          reject({ code: -32e3, message: "CDP tunnel closed" });
          return;
        }
        const id = this.nextId++;
        const timer = this.armTimeout(id, "cdp_attach timed out");
        this.pendingAttach = {
          id,
          pending: {
            resolve: /* @__PURE__ */ __name((result) => resolve5(result ?? {}), "resolve"),
            reject,
            timer
          }
        };
        try {
          this.sendToExtension({ type: CDP_FRAME_TYPES.attach, id });
        } catch (err) {
          clearTimeout(timer);
          this.pendingAttach = void 0;
          reject({
            code: -32e3,
            message: err instanceof Error ? err.message : String(err)
          });
        }
      }
    ).then(
      (info) => {
        this.emulator?.setTabInfo(info);
        return info;
      },
      (err) => {
        this.attached = false;
        this.onAttachFailure?.(
          err instanceof Error ? err.message : err?.message ?? String(err)
        );
        throw err;
      }
    );
    void this.attachPromise.finally(() => {
      this.attachPromise = void 0;
    }).catch(() => {
    });
    return this.attachPromise;
  }
  /**
   * Feed one inbound frame from the extension `/acp` socket. Returns true if
   * the frame was consumed by this link (so the WS layer can skip it).
   */
  handleInbound(frame) {
    if (!isCdpInboundFrame(frame)) return false;
    switch (frame.type) {
      case CDP_FRAME_TYPES.result:
        this.handleResult(frame);
        return true;
      case CDP_FRAME_TYPES.event:
        this.handleEvent(frame);
        return true;
      case CDP_FRAME_TYPES.attached:
        this.handleAttached(frame);
        return true;
      case CDP_FRAME_TYPES.detach:
        this.handleDetach(frame);
        return true;
      default:
        return false;
    }
  }
  handleResult(frame) {
    const id = typeof frame.id === "number" ? frame.id : void 0;
    if (id === void 0) {
      this.log?.("qwen serve: /cdp dropped cdp_result with non-numeric id");
      return;
    }
    if (frame.error) {
      this.settleReject(id, frame.error);
    } else {
      this.settleResolve(id, frame.result);
    }
  }
  handleEvent(frame) {
    if (!this.emulator) return;
    if (typeof frame.method !== "string") {
      this.log?.("qwen serve: /cdp dropped cdp_event with non-string method");
      return;
    }
    this.emulator.emitTabEvent(frame.method, frame.params);
  }
  handleAttached(frame) {
    const attach = this.pendingAttach;
    if (!attach || attach.id !== frame.id) {
      this.log?.(
        `qwen serve: /cdp dropped unexpected cdp_attached (id=${String(frame.id)})`
      );
      return;
    }
    this.pendingAttach = void 0;
    clearTimeout(attach.pending.timer);
    if (frame.error) {
      this.attached = false;
      attach.pending.reject({
        code: -32e3,
        message: frame.error.message ?? "cdp_attach failed"
      });
      return;
    }
    this.attached = true;
    attach.pending.resolve({ url: frame.url, title: frame.title });
  }
  handleDetach(frame) {
    const reason = typeof frame.reason === "string" ? frame.reason : "tab detached";
    this.attached = false;
    if (this.pendingAttach) {
      clearTimeout(this.pendingAttach.pending.timer);
      this.pendingAttach.pending.reject({ code: -32e3, message: reason });
      this.pendingAttach = void 0;
    }
    this.onDetach?.(reason);
  }
  armTimeout(id, message) {
    const timer = setTimeout(() => {
      this.settleReject(id, { code: -32e3, message });
      if (this.pendingAttach?.id === id) {
        this.pendingAttach.pending.reject({ code: -32e3, message });
        this.pendingAttach = void 0;
      }
    }, this.commandTimeoutMs);
    timer.unref?.();
    return timer;
  }
  armProgressLog(id, method) {
    if (!this.log) return void 0;
    const delay2 = Math.min(COMMAND_PROGRESS_LOG_MS, this.commandTimeoutMs);
    if (delay2 >= this.commandTimeoutMs) return void 0;
    const timer = setTimeout(() => {
      this.log?.(
        `qwen serve: /cdp still waiting for command id=${id} method=${method} after ${delay2}ms`
      );
    }, delay2);
    timer.unref?.();
    return timer;
  }
  settleResolve(id, result) {
    const p = this.pending.get(id);
    if (!p) return;
    clearTimeout(p.timer);
    if (p.progressTimer) clearTimeout(p.progressTimer);
    this.pending.delete(id);
    p.resolve(result);
  }
  settleReject(id, err) {
    const p = this.pending.get(id);
    if (!p) return;
    clearTimeout(p.timer);
    if (p.progressTimer) clearTimeout(p.progressTimer);
    this.pending.delete(id);
    p.reject(err);
  }
  /** In-flight forwarded-command count (for tests / accounting). */
  pendingCount() {
    return this.pending.size;
  }
  /** Reject all pending commands and stop accepting new ones. Idempotent. */
  dispose(reason = "CDP reverse link closed") {
    if (this.disposed) return;
    this.disposed = true;
    for (const [, p] of this.pending) {
      clearTimeout(p.timer);
      if (p.progressTimer) clearTimeout(p.progressTimer);
      p.reject({ code: -32e3, message: reason });
    }
    this.pending.clear();
    if (this.pendingAttach) {
      clearTimeout(this.pendingAttach.pending.timer);
      this.pendingAttach.pending.reject({ code: -32e3, message: reason });
      this.pendingAttach = void 0;
    }
    this.attached = false;
    this.attachPromise = void 0;
    this.emulator = void 0;
  }
};

// packages/cli/src/serve/cdp-tunnel/cdp-ws.ts
init_esbuild_shims();

// packages/cli/src/serve/acp-http/safe-ws-send.ts
init_esbuild_shims();
function safeWsSend(ws, payload, context = "frame") {
  if (ws.readyState === ws.OPEN) {
    try {
      ws.send(payload);
    } catch (err) {
      if (isServeDebugMode()) {
        try {
          writeStderrLine(
            `qwen serve: failed to send ${context} frame on /acp socket: ${err instanceof Error ? err.message : String(err)}`
          );
        } catch {
        }
      }
    }
    return;
  }
  if (isServeDebugMode()) {
    try {
      writeStderrLine(
        `qwen serve: dropped ${context} frame on non-OPEN /acp socket (readyState=${ws.readyState})`
      );
    } catch {
    }
  }
}
__name(safeWsSend, "safeWsSend");

// packages/cli/src/serve/cdp-tunnel/cdp-browser-emulator.ts
init_esbuild_shims();
var TAB_TARGET_ID = "qwen-cdp-tab";
var PAGE_TARGET_ID = "qwen-cdp-page";
var TAB_SESSION_ID = "qwen-cdp-tab-session";
var PAGE_SESSION_ID = "qwen-cdp-page-session";
var CDP_PROTOCOL_VERSION = "1.3";
var SERVER_ERROR = -32e3;
var CdpBrowserEmulator = class {
  constructor(cb, tab = {}) {
    this.cb = cb;
    this.tab = tab;
  }
  static {
    __name(this, "CdpBrowserEmulator");
  }
  pageSessionId = PAGE_SESSION_ID;
  /**
   * Refresh the synthetic tab/page targetInfo (url/title) once the extension
   * acks `cdp_attach`, so puppeteer's `page.url()`/`page.title()` reflect the
   * real page rather than the `about:blank` placeholder used before attach.
   */
  setTabInfo(info) {
    this.tab = { ...this.tab, ...info };
  }
  tabTargetInfo() {
    return {
      targetId: TAB_TARGET_ID,
      type: "tab",
      title: this.tab.title ?? "tab",
      url: this.tab.url ?? "about:blank",
      attached: false,
      canAccessOpener: false
    };
  }
  pageTargetInfo() {
    return {
      targetId: PAGE_TARGET_ID,
      type: "page",
      title: this.tab.title ?? "page",
      url: this.tab.url ?? "about:blank",
      attached: false,
      canAccessOpener: false
    };
  }
  /**
   * Handle one frame from the puppeteer client. Browser/tab-domain frames are
   * answered locally; page-session frames are forwarded to the real tab.
   */
  async handleFromClient(frame) {
    const { id, method, params, sessionId } = frame;
    if (!sessionId) {
      switch (method) {
        case "Browser.getVersion":
          return this.cb.reply({
            id,
            result: {
              protocolVersion: CDP_PROTOCOL_VERSION,
              product: "QwenCDPTunnel/1.0",
              revision: "@qwen",
              userAgent: "QwenCDPTunnel",
              jsVersion: "unknown"
            }
          });
        case "Target.getBrowserContexts":
          return this.cb.reply({ id, result: { browserContextIds: [] } });
        case "Target.setDiscoverTargets":
          this.cb.reply({
            method: "Target.targetCreated",
            params: { targetInfo: this.tabTargetInfo() }
          });
          this.cb.reply({
            method: "Target.targetCreated",
            params: { targetInfo: this.pageTargetInfo() }
          });
          return this.cb.reply({ id, result: {} });
        case "Target.setAutoAttach":
          this.cb.reply({
            method: "Target.attachedToTarget",
            params: {
              targetInfo: this.tabTargetInfo(),
              sessionId: TAB_SESSION_ID,
              waitingForDebugger: false
            }
          });
          return this.cb.reply({ id, result: {} });
        case "Target.getTargets":
          return this.cb.reply({
            id,
            result: {
              targetInfos: [this.tabTargetInfo(), this.pageTargetInfo()]
            }
          });
        case "Target.getTargetInfo":
          return this.cb.reply({
            id,
            result: { targetInfo: this.pageTargetInfo() }
          });
        default:
          this.cb.log?.(
            `qwen serve: /cdp unsupported browser-level CDP method: ${method ?? "(none)"}`
          );
          return this.cb.reply({ id, result: {} });
      }
    }
    if (sessionId === TAB_SESSION_ID) {
      if (method === "Target.setAutoAttach") {
        this.cb.reply({
          method: "Target.attachedToTarget",
          sessionId: TAB_SESSION_ID,
          params: {
            targetInfo: this.pageTargetInfo(),
            sessionId: PAGE_SESSION_ID,
            waitingForDebugger: false
          }
        });
        return this.cb.reply({ id, sessionId, result: {} });
      }
      return this.cb.reply({ id, sessionId, result: {} });
    }
    if (sessionId === PAGE_SESSION_ID) {
      try {
        const result = await this.cb.forwardToTab(method ?? "", params);
        return this.cb.reply({ id, sessionId, result });
      } catch (err) {
        const e = err;
        return this.cb.reply({
          id,
          sessionId,
          error: {
            code: e.code ?? SERVER_ERROR,
            message: e.message ?? "CDP forward failed",
            data: e.data
          }
        });
      }
    }
    return this.cb.reply({
      id,
      sessionId,
      error: {
        code: SERVER_ERROR,
        message: `Unknown CDP session: ${sessionId ?? "(none)"}`
      }
    });
  }
  /**
   * Re-emit a CDP event that arrived from the real tab (via the extension),
   * tagged with the page session id so puppeteer routes it to its Page.
   */
  emitTabEvent(method, params) {
    this.cb.reply({ method, params, sessionId: PAGE_SESSION_ID });
  }
};

// packages/cli/src/serve/cdp-tunnel/cdp-ws.ts
var CLOSE_NO_BRIDGE = 1011;
var CLOSE_NORMAL = 1e3;
var CDP_WS_HEARTBEAT_MS = 15e3;
function attachCdpClient(ws, registry, log) {
  const bridge = registry.getActive();
  if (!bridge) {
    log("qwen serve: /cdp rejected \u2014 no extension bridge connected");
    try {
      ws.close(
        CLOSE_NO_BRIDGE,
        "No browser extension connected to the CDP tunnel"
      );
    } catch {
    }
    return;
  }
  if (bridge.cdpBound) {
    log("qwen serve: /cdp rejected \u2014 a puppeteer client is already bound");
    try {
      ws.close(CLOSE_NO_BRIDGE, "A CDP client is already connected");
    } catch {
    }
    return;
  }
  bridge.cdpBound = true;
  const link = new CdpReverseLink(
    (frame) => bridge.send(frame),
    void 0,
    log
  );
  const emulator = new CdpBrowserEmulator({
    reply: /* @__PURE__ */ __name((frame) => {
      safeWsSend(ws, JSON.stringify(frame), "CDP");
    }, "reply"),
    forwardToTab: link.forwardToTab,
    log
  });
  link.bindEmulator(emulator);
  bridge.routeInbound = (frame) => link.handleInbound(frame);
  link.onDetach = (reason) => {
    log(`qwen serve: /cdp tab detached (${reason}); closing puppeteer socket`);
    try {
      ws.close(CLOSE_NORMAL, `tab detached: ${reason}`);
    } catch {
    }
  };
  let disposed = false;
  let heartbeatAlive = true;
  const heartbeat = setInterval(() => {
    if (disposed) return;
    if (!heartbeatAlive) {
      log("qwen serve: /cdp heartbeat missed; closing puppeteer socket");
      dispose("puppeteer /cdp heartbeat missed");
      try {
        ws.close(CLOSE_NORMAL, "cdp heartbeat missed");
      } catch {
      }
      return;
    }
    heartbeatAlive = false;
    try {
      ws.ping();
    } catch (err) {
      log(
        `qwen serve: /cdp heartbeat ping failed: ${err instanceof Error ? err.message : String(err)}`
      );
      dispose("puppeteer /cdp heartbeat ping failed");
      try {
        ws.close(CLOSE_NORMAL, "cdp heartbeat failed");
      } catch {
      }
    }
  }, CDP_WS_HEARTBEAT_MS);
  heartbeat.unref?.();
  const dispose = /* @__PURE__ */ __name((reason, notifyExtension = true) => {
    if (disposed) return;
    disposed = true;
    clearInterval(heartbeat);
    if (notifyExtension && registry.getActive() === bridge) {
      try {
        bridge.send({ type: CDP_FRAME_TYPES.release });
        log(
          `qwen serve: /cdp sent release to extension (puppeteer disconnected: ${reason})`
        );
      } catch (err) {
        log(
          `qwen serve: /cdp release send failed: ${err instanceof Error ? err.message : String(err)}`
        );
      }
    }
    link.dispose(reason);
    if (registry.getActive() === bridge) {
      bridge.routeInbound = () => false;
      bridge.cdpBound = false;
      bridge.onExtensionGone = void 0;
    }
  }, "dispose");
  link.onAttachFailure = (reason) => {
    log(`qwen serve: /cdp attach failed (${reason}); closing puppeteer socket`);
    dispose(`cdp_attach failed: ${reason}`, false);
    try {
      ws.close(CLOSE_NO_BRIDGE, "cdp attach failed");
    } catch {
    }
  };
  ws.on("pong", () => {
    heartbeatAlive = true;
  });
  bridge.onExtensionGone = () => {
    log("qwen serve: extension /acp dropped; closing puppeteer /cdp socket");
    dispose("extension /acp disconnected", false);
    try {
      ws.close(CLOSE_NORMAL, "extension disconnected");
    } catch {
    }
  };
  ws.on("message", (data) => {
    let frame;
    try {
      const text = typeof data === "string" ? data : data.toString("utf8");
      frame = JSON.parse(text);
    } catch {
      return;
    }
    void emulator.handleFromClient(frame).catch((err) => {
      log(
        `qwen serve: /cdp emulator error: ${err instanceof Error ? err.message : String(err)}`
      );
    });
  });
  ws.on("close", () => dispose("puppeteer /cdp socket closed"));
  ws.on("error", (err) => {
    log(
      `qwen serve: /cdp WS error: ${err instanceof Error ? err.message : String(err)}`
    );
    dispose("puppeteer /cdp socket error");
  });
  log("qwen serve: /cdp puppeteer client bound to extension bridge");
}
__name(attachCdpClient, "attachCdpClient");

// packages/cli/src/serve/acp-http/index.ts
var ACP_CONNECTION_HEADER = "acp-connection-id";
var ACP_SESSION_HEADER = "acp-session-id";
var CDP_PATH = "/cdp";
var CDP_BRIDGE_CLIENT_NAME = "qwen-cdp-bridge";
var CHROME_DEVTOOLS_MCP_SERVER_NAME = "chrome-devtools";
var RUNTIME_MCP_RETRY_DELAY_MS = 250;
var RUNTIME_MCP_RETRY_ATTEMPTS = 20;
var requireFromHere = createRequire(import.meta.url);
function formatCdpEndpointHost(hostname) {
  const host = hostname?.trim() || "127.0.0.1";
  if (host === "0.0.0.0" || host === "::" || host === "[::]") {
    return "127.0.0.1";
  }
  const unbracketed = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
  return unbracketed.includes(":") ? `[${unbracketed}]` : unbracketed;
}
__name(formatCdpEndpointHost, "formatCdpEndpointHost");
function isAcpChannelUnavailable(err) {
  return err?.data?.errorKind === "acp_channel_unavailable";
}
__name(isAcpChannelUnavailable, "isAcpChannelUnavailable");
function delay(ms) {
  return new Promise((resolve5) => setTimeout(resolve5, ms));
}
__name(delay, "delay");
function buildChromeDevToolsMcpRuntimeConfigFromPackage(localPort, pkgJsonPath, pkgBin, hostname) {
  if (localPort === void 0 || !Number.isInteger(localPort) || localPort <= 0) {
    return void 0;
  }
  const binRel = typeof pkgBin === "string" ? pkgBin : Object.values(pkgBin ?? {})[0];
  if (!binRel) return void 0;
  const pkgDir = path9.dirname(pkgJsonPath);
  const binPath = path9.resolve(pkgDir, binRel);
  const binRelToPkg = path9.relative(pkgDir, binPath);
  if (binRelToPkg.startsWith("..") || path9.isAbsolute(binRelToPkg)) {
    return void 0;
  }
  return {
    command: process.execPath,
    args: [
      binPath,
      "--wsEndpoint",
      `ws://${formatCdpEndpointHost(hostname)}:${localPort}/cdp`
    ],
    alwaysLoadTools: true,
    [RUNTIME_MCP_IF_ABSENT_CONFIG_FLAG]: true
  };
}
__name(buildChromeDevToolsMcpRuntimeConfigFromPackage, "buildChromeDevToolsMcpRuntimeConfigFromPackage");
function buildChromeDevToolsMcpRuntimeConfig(localPort, hostname) {
  try {
    const pkgJsonPath = requireFromHere.resolve(
      "chrome-devtools-mcp/package.json"
    );
    const pkg = requireFromHere("chrome-devtools-mcp/package.json");
    return buildChromeDevToolsMcpRuntimeConfigFromPackage(
      localPort,
      pkgJsonPath,
      pkg.bin,
      hostname
    );
  } catch (err) {
    writeStderrLine(
      `qwen serve: chrome-devtools-mcp package not resolvable: ${err instanceof Error ? err.message : String(err)}`
    );
    return void 0;
  }
}
__name(buildChromeDevToolsMcpRuntimeConfig, "buildChromeDevToolsMcpRuntimeConfig");
var WS_BEARER_SUBPROTOCOL_PREFIX = "qwen-bearer.";
function extractUpgradeBearer(req) {
  const authHeader = req.headers["authorization"];
  if (authHeader && authHeader.includes(" ")) {
    const scheme = authHeader.slice(0, authHeader.indexOf(" ")).toLowerCase();
    if (scheme === "bearer") {
      const credentials = authHeader.slice(authHeader.indexOf(" ") + 1).trim();
      if (credentials) return credentials;
    }
  }
  const offered = req.headers["sec-websocket-protocol"];
  if (offered) {
    for (const raw of offered.split(",")) {
      const entry = raw.trim();
      if (!entry.startsWith(WS_BEARER_SUBPROTOCOL_PREFIX)) continue;
      const encoded = entry.slice(WS_BEARER_SUBPROTOCOL_PREFIX.length);
      const decoded = Buffer.from(encoded, "base64url").toString("utf8");
      if (decoded) return decoded;
    }
  }
  return void 0;
}
__name(extractUpgradeBearer, "extractUpgradeBearer");
var CONN_GRACE_MS = 1e4;
var SESSION_GRACE_MS = 1e4;
var WS_EXEMPT_METHODS = /* @__PURE__ */ new Set([
  "_qwen/session/heartbeat",
  "_qwen/session/update_metadata"
]);
var WS_READ_METHODS = /* @__PURE__ */ new Set([
  "session/list",
  "_qwen/session/context",
  "_qwen/session/supported_commands",
  "_qwen/session/context_usage",
  "_qwen/session/tasks",
  "_qwen/session/lsp",
  "_qwen/workspace/mcp",
  "_qwen/workspace/skills",
  "_qwen/workspace/providers",
  "_qwen/workspace/env",
  "_qwen/workspace/preflight",
  "_qwen/workspace/trust",
  "_qwen/workspace/permissions",
  "_qwen/workspace/voice",
  "_qwen/workspace/tools",
  "_qwen/workspace/mcp/tools",
  "_qwen/workspace/mcp/resources",
  "_qwen/workspace/agents/list",
  "_qwen/workspace/agents/get",
  "_qwen/workspace/memory",
  "_qwen/workspace/memory/remember/get",
  "_qwen/workspace/auth/status",
  "_qwen/workspace/auth/device_flow/get",
  "_qwen/file/read",
  "_qwen/file/read_bytes",
  "_qwen/file/stat",
  "_qwen/file/list",
  "_qwen/file/glob"
]);
function isSameLoopbackOrigin(origin, localPort) {
  if (!localPort) return false;
  const parsed = new URL(origin);
  const allowed = /* @__PURE__ */ new Set([
    `http://localhost:${localPort}`,
    `http://127.0.0.1:${localPort}`,
    `http://[::1]:${localPort}`
  ]);
  return allowed.has(parsed.origin.toLowerCase());
}
__name(isSameLoopbackOrigin, "isSameLoopbackOrigin");
var MAX_INFLIGHT_MCP_DISPATCH = 8;
function mountAcpHttp(app, bridge, opts) {
  const enabled = opts.enabled ?? process.env["QWEN_SERVE_ACP_HTTP"] !== "0";
  if (!enabled) return void 0;
  const path13 = opts.path ?? "/acp";
  const dispatcherRef = {};
  const registry = new ConnectionRegistry(
    (req, clientId) => {
      if (!dispatcherRef.current) {
        writeStderrLine(
          "qwen serve: /acp abandonPending called before dispatcher initialized (skipped)"
        );
        return false;
      }
      return dispatcherRef.current.cancelAbandonedPermission(req, clientId);
    },
    // Best-effort bridge detach so a torn-down connection's bridge-stamped
    // client ids don't linger in the bridge's voter/known-client sets.
    (sessionId, clientId) => {
      void bridge.detachClient(sessionId, clientId).catch((err) => {
        writeStderrLine(
          `qwen serve: /acp detachClient(${sessionId}) failed: ${err instanceof Error ? err.message : String(err)}`
        );
      });
    },
    opts.maxConnections
  );
  let cdpMcpRegistered = false;
  let cdpMcpRegistering;
  let cdpMcpTerminalSkipLogged = false;
  async function removeChromeDevToolsMcp(originatorClientId) {
    if (!cdpMcpRegistered) return;
    cdpMcpRegistered = false;
    try {
      await bridge.removeRuntimeMcpServer(
        CHROME_DEVTOOLS_MCP_SERVER_NAME,
        originatorClientId
      );
    } catch (err) {
      writeStderrLine(
        `qwen serve: failed to remove ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP: ${err instanceof Error ? err.message : String(err)}`
      );
    }
  }
  __name(removeChromeDevToolsMcp, "removeChromeDevToolsMcp");
  function removeChromeDevToolsMcpIfUnused(originatorClientId) {
    void (async () => {
      if (opts.cdpTunnelRegistry?.hasActive()) return;
      if (cdpMcpRegistering) await cdpMcpRegistering;
      if (opts.cdpTunnelRegistry?.hasActive()) return;
      await removeChromeDevToolsMcp(originatorClientId);
    })().catch((err) => {
      writeStderrLine(
        `qwen serve: failed to clean up ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP: ${err instanceof Error ? err.message : String(err)}`
      );
    });
  }
  __name(removeChromeDevToolsMcpIfUnused, "removeChromeDevToolsMcpIfUnused");
  function ensureChromeDevToolsMcpRegistered(localPort, originatorClientId) {
    if (opts.token) {
      if (!cdpMcpTerminalSkipLogged) {
        writeStderrLine(
          `qwen serve: ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP skipped because /cdp requires bearer auth`
        );
        cdpMcpTerminalSkipLogged = true;
      }
      return;
    }
    if (cdpMcpRegistered || cdpMcpRegistering || cdpMcpTerminalSkipLogged) {
      return;
    }
    const runtimeConfig = buildChromeDevToolsMcpRuntimeConfig(
      localPort,
      opts.hostname
    );
    if (!runtimeConfig) {
      cdpMcpTerminalSkipLogged = true;
      return;
    }
    cdpMcpRegistering = (async () => {
      try {
        let result;
        for (let attempt = 0; ; attempt++) {
          try {
            result = await bridge.addRuntimeMcpServer(
              CHROME_DEVTOOLS_MCP_SERVER_NAME,
              runtimeConfig,
              originatorClientId
            );
            break;
          } catch (err) {
            if (!isAcpChannelUnavailable(err) || attempt >= RUNTIME_MCP_RETRY_ATTEMPTS || !opts.cdpTunnelRegistry?.hasActive()) {
              throw err;
            }
            await delay(RUNTIME_MCP_RETRY_DELAY_MS);
          }
        }
        if (result.skipped) {
          writeStderrLine(
            `qwen serve: ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP skipped: ${result.reason ?? "unknown"}`
          );
          return;
        }
        if (result.shadowedSettings) {
          await bridge.removeRuntimeMcpServer(
            CHROME_DEVTOOLS_MCP_SERVER_NAME,
            originatorClientId
          ).catch(() => {
          });
          cdpMcpTerminalSkipLogged = true;
          writeStderrLine(
            `qwen serve: ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP skipped because settings already define it`
          );
          return;
        }
        cdpMcpRegistered = true;
        if (!opts.cdpTunnelRegistry?.hasActive()) {
          await removeChromeDevToolsMcp(originatorClientId);
        }
      } catch (err) {
        writeStderrLine(
          `qwen serve: failed to add ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP: ${err instanceof Error ? err.message : String(err)}`
        );
      } finally {
        cdpMcpRegistering = void 0;
      }
    })();
  }
  __name(ensureChromeDevToolsMcpRegistered, "ensureChromeDevToolsMcpRegistered");
  const dispatcher = new AcpDispatcher(
    bridge,
    opts.boundWorkspace,
    opts.workspace,
    opts.workspaceRememberLane,
    opts.fsFactory,
    opts.deviceFlowRegistry,
    opts.sessionShellCommandEnabled === true,
    registry,
    opts.archiveCoordinator ?? new SessionArchiveCoordinator()
  );
  dispatcherRef.current = dispatcher;
  app.post(path13, async (req, res) => {
    const ct = req.headers["content-type"];
    if (!ct || !ct.startsWith("application/json")) {
      res.status(415).json({ error: "Content-Type must be application/json" });
      return;
    }
    if (Array.isArray(req.body)) {
      res.status(501).json({ error: "Batch JSON-RPC requests are not supported" });
      return;
    }
    const parsed = parseInbound(req.body);
    if (!parsed.ok) {
      writeStderrLine(
        `qwen serve: /acp malformed request from ${req.socket?.remoteAddress}: ${parsed.error.error.message}`
      );
      res.status(400).json(parsed.error);
      return;
    }
    const message = parsed.message;
    if (isRequest(message) && message.method === "initialize") {
      const conn2 = registry.create(isLoopbackReq(req));
      if (!conn2) {
        writeStderrLine(
          `qwen serve: /acp connection cap reached (max=${registry.connectionCap}), rejecting initialize`
        );
        res.setHeader("Retry-After", "5");
        res.status(503).json(
          error(
            message.id,
            RPC.INTERNAL_ERROR,
            "Too many ACP connections; retry later"
          )
        );
        return;
      }
      const requestedVersion = message.params && typeof message.params === "object" && !Array.isArray(message.params) ? message.params["protocolVersion"] : void 0;
      res.setHeader("Acp-Connection-Id", conn2.connectionId);
      res.status(200).json({
        // success envelope: clients correlate by the request id.
        jsonrpc: "2.0",
        id: message.id,
        result: dispatcher.buildInitializeResult(
          conn2.connectionId,
          requestedVersion
        )
      });
      writeStderrLine(
        `qwen serve: /acp connection established ${conn2.connectionId.slice(0, 8)} (loopback=${conn2.fromLoopback}, active=${registry.size})`
      );
      return;
    }
    const connHeader = headerOf(req, ACP_CONNECTION_HEADER);
    if (!connHeader) {
      res.status(400).json(
        error(
          isRequest(message) ? message.id : null,
          RPC.INVALID_REQUEST,
          "Missing Acp-Connection-Id"
        )
      );
      return;
    }
    const conn = registry.get(connHeader);
    if (!conn) {
      res.status(404).json(
        error(
          isRequest(message) ? message.id : null,
          RPC.INVALID_REQUEST,
          "Unknown Acp-Connection-Id"
        )
      );
      return;
    }
    if (opts.checkRate && isRequest(message)) {
      const m = message.method;
      if (!WS_EXEMPT_METHODS.has(m)) {
        const tier = m === "session/prompt" || m === "_qwen/session/prompt" ? "prompt" : WS_READ_METHODS.has(m) ? "read" : "mutation";
        const httpKey = (req.socket?.remoteAddress ?? "http-unknown").replace(
          /^::ffff:/,
          ""
        );
        if (!opts.checkRate(httpKey, tier)) {
          res.setHeader("Retry-After", "5");
          res.status(429).json({
            error: "Rate limit exceeded",
            code: "rate_limit_exceeded",
            tier
          });
          return;
        }
      }
    }
    res.status(202).end();
    await dispatcher.handle(
      conn,
      message,
      headerOf(req, ACP_SESSION_HEADER),
      isLoopbackReq(req)
    ).catch((err) => {
      writeStderrLine(
        `qwen serve: /acp handle error: ${err instanceof Error ? err.message : String(err)}`
      );
    });
  });
  app.get(path13, (req, res) => {
    const accept = req.headers["accept"] ?? "";
    if (!accept.includes("text/event-stream")) {
      res.status(406).json({ error: "Accept header must include text/event-stream" });
      return;
    }
    const connHeader = headerOf(req, ACP_CONNECTION_HEADER);
    if (!connHeader) {
      res.status(400).json({ error: "Missing Acp-Connection-Id" });
      return;
    }
    const conn = registry.get(connHeader);
    if (!conn) {
      res.status(404).json({ error: "Unknown Acp-Connection-Id" });
      return;
    }
    const sessionId = headerOf(req, ACP_SESSION_HEADER);
    if (!sessionId) {
      const connId = conn.connectionId;
      const stream2 = new SseStream(
        res,
        () => {
          writeStderrLine(
            `qwen serve: /acp connection stream closed (${connId.slice(0, 8)})`
          );
          conn.clearGraceTimer();
          const reapConnIfDead = /* @__PURE__ */ __name(() => {
            if (registry.get(connId) === conn && conn.connStream === stream2 && conn.connGraceExpired && !conn.hasLiveSessionStream() && !conn.hasRecoverableSession()) {
              writeStderrLine(
                `qwen serve: /acp reaping connection ${connId.slice(0, 8)} (conn stream gone, no live session stream)`
              );
              registry.delete(connId);
            }
          }, "reapConnIfDead");
          conn.connGraceTimer = setTimeout(() => {
            conn.connGraceExpired = true;
            reapConnIfDead();
          }, CONN_GRACE_MS);
          conn.connGraceTimer.unref?.();
          conn.onSessionGraceExpired = reapConnIfDead;
        },
        () => conn.touch()
      );
      stream2.open();
      conn.attachConnStream(stream2);
      return;
    }
    if (!conn.ownsSession(sessionId)) {
      res.status(403).json({ error: "Session not owned by this connection" });
      return;
    }
    const ac = new AbortController();
    const stream = new SseStream(
      res,
      () => {
        ac.abort();
      },
      () => conn.touch()
    );
    stream.open();
    const lastEventId = parseLastEventId2(
      headerOf(req, "last-event-id"),
      "/acp "
    );
    conn.attachSessionStream(sessionId, stream, ac, lastEventId);
    const onPumpSettled = /* @__PURE__ */ __name(() => {
      if (stream.isClosed) {
        conn.detachSessionStream(sessionId, stream, SESSION_GRACE_MS);
      } else if (conn.sessions.get(sessionId)?.stream === stream) {
        writeStderrLine(
          `qwen serve: /acp session stream pump ended while open (${logSafe(sessionId)}) \u2014 closing`
        );
        conn.closeSessionStream(sessionId);
      } else {
        writeStderrLine(
          `qwen serve: /acp session stream pump settled for a superseded stream (${logSafe(sessionId)}) \u2014 no-op`
        );
      }
    }, "onPumpSettled");
    void dispatcher.pumpSessionEvents(conn, sessionId, ac.signal, lastEventId).then(onPumpSettled, (err) => {
      writeStderrLine(
        `qwen serve: /acp event pump error (${logSafe(sessionId)}, lastEventId=${lastEventId ?? "none"}): ${logSafe(err instanceof Error ? err.message : String(err))}`
      );
      onPumpSettled();
    });
  });
  app.delete(path13, (req, res) => {
    const connectionId = headerOf(req, ACP_CONNECTION_HEADER);
    if (!connectionId) {
      res.status(400).json({ error: "Missing Acp-Connection-Id" });
      return;
    }
    const existed = registry.delete(connectionId);
    if (existed) {
      writeStderrLine(
        `qwen serve: /acp connection deleted ${connectionId.slice(0, 8)} (remaining=${registry.size})`
      );
    }
    res.status(202).end();
  });
  let wss;
  let upgradeListener;
  let upgradeServer;
  function setupWebSocket(httpServer) {
    if (wss) return;
    wss = new import_websocket_server.default({
      noServer: true,
      maxPayload: 10 * 1024 * 1024,
      // Browsers authenticate the upgrade by offering the bearer token as a
      // `qwen-bearer.*` subprotocol (see extractUpgradeBearer). Never echo that
      // secret-bearing value back in the handshake response — select the first
      // non-secret subprotocol instead. The web-shell offers a non-secret
      // marker (`qwen-ws`) alongside the bearer one precisely so there is always
      // a safe value to select: selecting none would make strict WS clients
      // (e.g. the `ws` library) reject the handshake with "Server sent no
      // subprotocol". ACP clients offer no subprotocol, so this is a no-op for
      // them.
      handleProtocols: /* @__PURE__ */ __name((protocols) => {
        for (const proto of protocols) {
          if (!proto.startsWith(WS_BEARER_SUBPROTOCOL_PREFIX)) return proto;
        }
        return false;
      }, "handleProtocols")
    });
    upgradeServer = httpServer;
    const expectedTokenHash = opts.token ? createHash3("sha256").update(opts.token).digest() : void 0;
    upgradeListener = /* @__PURE__ */ __name((req, socket, head) => {
      const rawAddr = socket.remoteAddress ?? "ws-unknown";
      const localPort = socket.localPort;
      const logReject = /* @__PURE__ */ __name((reason) => {
        writeStderrLine(
          `qwen serve: WebSocket upgrade rejected (${reason}) from ${rawAddr}`
        );
      }, "logReject");
      let url;
      try {
        url = new URL(
          req.url ?? "/",
          `http://${req.headers.host ?? "localhost"}`
        );
      } catch {
        logReject("invalid-url");
        socket.destroy();
        return;
      }
      const isCdpPath = opts.cdpTunnelOverWs === true && opts.cdpTunnelRegistry !== void 0 && url.pathname === CDP_PATH;
      const extraRoute = opts.extraWsRoutes?.find(
        (route) => route.path === url.pathname
      );
      if (url.pathname !== path13 && !isCdpPath && !extraRoute) {
        logReject(`unknown-path ${url.pathname}`);
        socket.destroy();
        return;
      }
      const fromLoopback = isLoopbackSocket(socket);
      if (fromLoopback) {
        const host = (req.headers["host"] ?? "").toLowerCase();
        const allowed = /* @__PURE__ */ new Set([
          `localhost:${localPort}`,
          `127.0.0.1:${localPort}`,
          `[::1]:${localPort}`,
          `host.docker.internal:${localPort}`
        ]);
        if (!allowed.has(host)) {
          logReject(`host-not-allowed ${host || "(missing)"}`);
          socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
          socket.destroy();
          return;
        }
      }
      const origin = req.headers["origin"];
      if (origin) {
        try {
          const isLoopbackOrigin = isSameLoopbackOrigin(origin, localPort);
          const isAllowlistedOrigin = opts.allowedOrigins !== void 0 && opts.allowedOrigins.origins.has(origin.toLowerCase());
          if (!isLoopbackOrigin && !isAllowlistedOrigin) {
            logReject(`origin-not-allowed ${origin}`);
            socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
            socket.destroy();
            return;
          }
        } catch {
          logReject("invalid-origin");
          socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
          socket.destroy();
          return;
        }
      }
      if (opts.token) {
        const credentials = extractUpgradeBearer(req);
        const actual = credentials ? createHash3("sha256").update(credentials).digest() : void 0;
        if (!actual || !expectedTokenHash || actual.length !== expectedTokenHash.length || !timingSafeEqual(expectedTokenHash, actual)) {
          logReject("auth-mismatch");
          socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
          socket.destroy();
          return;
        }
      } else if (!fromLoopback) {
        logReject("non-loopback-without-token");
        socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
        socket.destroy();
        return;
      }
      if (isCdpPath) {
        wss.handleUpgrade(req, socket, head, (ws) => {
          attachCdpClient(ws, opts.cdpTunnelRegistry, writeStderrLine);
        });
        return;
      }
      wss.handleUpgrade(req, socket, head, (ws) => {
        if (extraRoute) {
          extraRoute.onConnection(ws, req);
          return;
        }
        let initialized = false;
        const initTimer = setTimeout(() => {
          if (!initialized) {
            writeStderrLine(
              `qwen serve: /acp WS initialize timeout (30s) from ${rawAddr}`
            );
            ws.close(1002, "Initialize timeout");
          }
        }, 3e4);
        initTimer.unref?.();
        let connRef;
        let messageQueue = Promise.resolve();
        let clientMcp;
        let clientMcpInflightDispatch = 0;
        let cdpBridgeUnregister;
        let cdpEndpoint;
        const wsKey = rawAddr.startsWith("::ffff:") ? rawAddr.slice(7) : rawAddr;
        ws.on("error", (err) => {
          writeStderrLine(
            `qwen serve: /acp WS error: ${err instanceof Error ? err.message : String(err)}`
          );
        });
        ws.on("close", () => {
          if (clientMcp) {
            void clientMcp.dispose("WS closed").catch(() => {
            });
            clientMcp = void 0;
          }
          if (cdpBridgeUnregister) {
            cdpBridgeUnregister();
            cdpBridgeUnregister = void 0;
            removeChromeDevToolsMcpIfUnused(
              connRef?.connectionId ?? "cdp-bridge"
            );
          }
        });
        ws.on("message", (rawData) => {
          messageQueue = messageQueue.then(() => handleWsMessage(rawData)).catch((err) => {
            writeStderrLine(
              `qwen serve: /acp WS message handler error: ${err instanceof Error ? err.message : String(err)}`
            );
          });
        });
        async function handleWsMessage(rawData) {
          let text;
          try {
            text = typeof rawData === "string" ? rawData : rawData.toString("utf8");
          } catch {
            ws.close(1003, "Only text frames supported");
            return;
          }
          let parsed;
          try {
            parsed = JSON.parse(text);
          } catch {
            ws.send(
              JSON.stringify(error(null, RPC.PARSE_ERROR, "Parse error"))
            );
            return;
          }
          if (Array.isArray(parsed)) {
            ws.send(
              JSON.stringify({
                error: "Batch JSON-RPC not supported"
              })
            );
            return;
          }
          if (opts.clientMcpOverWs === true && parsed !== null && typeof parsed === "object" && ClientMcpWsConnection.isClientMcpFrameType(
            parsed.type
          )) {
            if (!initialized) {
              ws.send(
                JSON.stringify({
                  type: "mcp_error",
                  code: "not_initialized",
                  message: "initialize the ACP connection before mcp_register"
                })
              );
              return;
            }
            const frameType = parsed.type;
            if (opts.checkRate) {
              const tier = frameType === "mcp_message" ? "read" : "mutation";
              if (!opts.checkRate(wsKey, tier)) {
                safeWsSend(
                  ws,
                  JSON.stringify({
                    type: "mcp_error",
                    code: "rate_limited",
                    message: "Rate limit exceeded"
                  }),
                  "client-MCP"
                );
                return;
              }
            }
            if (!clientMcp) {
              const provider = opts.clientMcpProviderFactory ? opts.clientMcpProviderFactory(
                connRef?.connectionId ?? "ws-unknown"
              ) : opts.clientMcpProvider;
              clientMcp = new ClientMcpWsConnection(
                (frame) => safeWsSend(ws, JSON.stringify(frame), "client-MCP"),
                provider
              );
            }
            const sendClientMcpAck = /* @__PURE__ */ __name((result) => {
              if (result.kind === "registered") {
                safeWsSend(
                  ws,
                  JSON.stringify({
                    type: "mcp_registered",
                    server: result.server,
                    toolCount: result.toolCount
                  }),
                  "client-MCP"
                );
              } else if (result.kind === "unregistered") {
                safeWsSend(
                  ws,
                  JSON.stringify({
                    type: "mcp_unregistered",
                    server: result.server
                  }),
                  "client-MCP"
                );
              } else if (result.kind === "error") {
                safeWsSend(
                  ws,
                  JSON.stringify({
                    type: "mcp_error",
                    code: result.code,
                    message: result.message
                  }),
                  "client-MCP"
                );
              }
            }, "sendClientMcpAck");
            const dispatchOffQueue = frameType !== "mcp_message";
            if (dispatchOffQueue) {
              if (clientMcpInflightDispatch >= MAX_INFLIGHT_MCP_DISPATCH) {
                writeStderrLine(
                  `qwen serve: /acp client-MCP inflight cap hit (${MAX_INFLIGHT_MCP_DISPATCH}); rejecting ${String(frameType)} frame`
                );
                safeWsSend(
                  ws,
                  JSON.stringify({
                    type: "mcp_error",
                    code: "too_many_inflight",
                    message: `too many concurrent client-MCP registrations (max ${MAX_INFLIGHT_MCP_DISPATCH})`
                  }),
                  "client-MCP"
                );
                return;
              }
              clientMcpInflightDispatch++;
            }
            const handleP = clientMcp.handleFrame(parsed).then(sendClientMcpAck, (err) => {
              const message2 = err instanceof Error ? err.message : String(err);
              writeStderrLine(
                `qwen serve: /acp client-mcp frame error: ${message2}`
              );
              if (dispatchOffQueue) {
                safeWsSend(
                  ws,
                  JSON.stringify({
                    type: "mcp_error",
                    code: "internal_error",
                    message: message2
                  }),
                  "client-MCP"
                );
              }
            }).finally(() => {
              if (dispatchOffQueue) clientMcpInflightDispatch--;
            });
            if (frameType === "mcp_message") {
              await handleP;
            }
            return;
          }
          if (opts.cdpTunnelOverWs === true && cdpEndpoint !== void 0 && parsed !== null && typeof parsed === "object" && isCdpInboundFrameType(parsed.type)) {
            cdpEndpoint.routeInbound(parsed);
            return;
          }
          const inbound = parseInbound(parsed);
          if (!inbound.ok) {
            ws.send(JSON.stringify(inbound.error));
            return;
          }
          const message = inbound.message;
          if (!initialized) {
            if (!isRequest(message) || message.method !== "initialize") {
              ws.send(
                JSON.stringify(
                  error(
                    isRequest(message) ? message.id : null,
                    RPC.INVALID_REQUEST,
                    "First message must be initialize"
                  )
                )
              );
              ws.close(1002, "Protocol error");
              return;
            }
            const conn2 = registry.create(fromLoopback);
            if (!conn2) {
              ws.send(
                JSON.stringify(
                  error(
                    message.id,
                    RPC.INTERNAL_ERROR,
                    "Too many connections"
                  )
                )
              );
              ws.close(1013, "Connection cap");
              return;
            }
            const requestedVersion = message.params && typeof message.params === "object" && !Array.isArray(message.params) ? message.params["protocolVersion"] : void 0;
            const stream = new WsStream(
              ws,
              () => {
                writeStderrLine(
                  `qwen serve: /acp WS closed (${conn2.connectionId.slice(0, 8)})`
                );
                registry.delete(conn2.connectionId);
              },
              () => conn2.touch()
            );
            conn2.attachConnStream(stream);
            ws.send(
              JSON.stringify({
                jsonrpc: "2.0",
                id: message.id,
                result: dispatcher.buildInitializeResult(
                  conn2.connectionId,
                  requestedVersion
                )
              })
            );
            initialized = true;
            clearTimeout(initTimer);
            connRef = conn2;
            writeStderrLine(
              `qwen serve: /acp WS established ${conn2.connectionId.slice(0, 8)} (loopback=${fromLoopback}, active=${registry.size})`
            );
            const clientName = message.params && typeof message.params === "object" && !Array.isArray(message.params) ? message.params["clientInfo"]?.name : void 0;
            if (opts.cdpTunnelOverWs === true && opts.cdpTunnelRegistry !== void 0 && clientName === CDP_BRIDGE_CLIENT_NAME) {
              cdpEndpoint = {
                connectionId: conn2.connectionId,
                send: /* @__PURE__ */ __name((frame) => safeWsSend(ws, JSON.stringify(frame), "CDP"), "send"),
                routeInbound: /* @__PURE__ */ __name(() => false, "routeInbound")
              };
              cdpBridgeUnregister = opts.cdpTunnelRegistry.register(cdpEndpoint);
              ensureChromeDevToolsMcpRegistered(localPort, conn2.connectionId);
              writeStderrLine(
                `qwen serve: /acp connection ${conn2.connectionId.slice(0, 8)} registered as CDP bridge`
              );
            }
            return;
          }
          const conn = connRef;
          if (!conn || conn.destroyed) {
            ws.send(
              JSON.stringify(
                error(null, RPC.INTERNAL_ERROR, "Connection lost")
              )
            );
            ws.close(1011, "Connection lost");
            return;
          }
          if (isRequest(message) && message.params && typeof message.params === "object") {
            const sid = message.params["sessionId"];
            if (typeof sid === "string" && conn.ownsSession(sid)) {
              const binding = conn.sessions.get(sid);
              if (binding && !binding.stream && conn.connStream && !conn.connStream.isClosed) {
                const ac = new AbortController();
                conn.attachSessionStream(sid, conn.connStream, ac);
                const myAbort = ac;
                const cleanupSession = /* @__PURE__ */ __name(() => {
                  const b = conn.sessions.get(sid);
                  if (b?.stream === conn.connStream && b?.abort === myAbort) {
                    conn.closeSessionStream(sid);
                  }
                }, "cleanupSession");
                void dispatcher.pumpSessionEvents(conn, sid, ac.signal).then(cleanupSession, (err) => {
                  writeStderrLine(
                    `qwen serve: /acp WS pump error (${sid}): ${err instanceof Error ? err.message : String(err)}`
                  );
                  cleanupSession();
                });
              }
            }
          }
          if (opts.checkRate && isRequest(message)) {
            const m = message.method;
            if (WS_EXEMPT_METHODS.has(m)) {
            } else {
              const tier = m === "session/prompt" || m === "_qwen/session/prompt" ? "prompt" : WS_READ_METHODS.has(m) ? "read" : "mutation";
              if (!opts.checkRate(wsKey, tier)) {
                ws.send(
                  JSON.stringify(
                    error(
                      message.id,
                      RPC.INTERNAL_ERROR,
                      "Rate limit exceeded"
                    )
                  )
                );
                return;
              }
            }
          }
          const isPrompt = isRequest(message) && (message.method === "session/prompt" || message.method === "_qwen/session/prompt");
          const dispatchP = dispatcher.handle(conn, message, void 0, fromLoopback).catch((err) => {
            writeStderrLine(
              `qwen serve: /acp WS handle error: ${err instanceof Error ? err.message : String(err)}`
            );
          });
          if (!isPrompt) await dispatchP;
        }
        __name(handleWsMessage, "handleWsMessage");
      });
    }, "upgradeListener");
    httpServer.on("upgrade", upgradeListener);
    writeStderrLine(`qwen serve: /acp WebSocket transport enabled on ${path13}`);
  }
  __name(setupWebSocket, "setupWebSocket");
  return {
    dispose: /* @__PURE__ */ __name(() => {
      if (upgradeServer && upgradeListener) {
        upgradeServer.removeListener("upgrade", upgradeListener);
        upgradeListener = void 0;
        upgradeServer = void 0;
      }
      registry.dispose();
      if (wss) {
        wss.close();
        wss = void 0;
      }
    }, "dispose"),
    registry,
    attachServer(server) {
      setupWebSocket(server);
    }
  };
}
__name(mountAcpHttp, "mountAcpHttp");
function headerOf(req, name) {
  const v = req.headers[name];
  return Array.isArray(v) ? v[0] : v;
}
__name(headerOf, "headerOf");
function isLoopbackSocket(socket) {
  const addr = socket.remoteAddress;
  if (typeof addr !== "string") return false;
  return addr === "::1" || addr.startsWith("127.") || addr.startsWith("::ffff:127.");
}
__name(isLoopbackSocket, "isLoopbackSocket");
function isLoopbackReq(req) {
  const addr = req.socket?.remoteAddress;
  if (typeof addr !== "string") return false;
  return addr === "::1" || addr.startsWith("127.") || addr.startsWith("::ffff:127.");
}
__name(isLoopbackReq, "isLoopbackReq");

// packages/cli/src/serve/voice/voice-ws.ts
init_esbuild_shims();

// packages/cli/src/serve/voice/resolve-voice-config.ts
init_esbuild_shims();
function readVoiceModel2(settings) {
  const raw = settings.merged.voiceModel;
  if (typeof raw !== "string") return void 0;
  const trimmed = raw.trim();
  return trimmed.length > 0 ? trimmed : void 0;
}
__name(readVoiceModel2, "readVoiceModel");
function buildModelsConfig(settings) {
  const merged = settings.merged;
  const selectedAuthType = merged.security?.auth?.selectedType ?? getAuthTypeFromEnv();
  const resolvedCliConfig = resolveCliGenerationConfig({
    argv: {},
    settings: merged,
    selectedAuthType,
    env: process.env
  });
  return new ModelsConfig({
    initialAuthType: selectedAuthType,
    modelProvidersConfig: merged.modelProviders,
    generationConfig: resolvedCliConfig.generationConfig,
    generationConfigSources: resolvedCliConfig.sources
  });
}
__name(buildModelsConfig, "buildModelsConfig");
function loadDaemonVoiceContext(workspaceCwd) {
  const settings = loadSettings(workspaceCwd);
  const voiceModel = readVoiceModel2(settings);
  if (!voiceModel) {
    throw new Error("No voice model is configured for this workspace.");
  }
  const models = buildModelsConfig(settings);
  resolveVoiceTranscriptionConfig({ config: models, settings, voiceModel });
  return {
    settings,
    models,
    voiceModel,
    streaming: isStreamingVoiceModel(voiceModel)
  };
}
__name(loadDaemonVoiceContext, "loadDaemonVoiceContext");

// packages/cli/src/serve/voice/voice-ws.ts
var debugLogger = createDebugLogger("VOICE_WS");
var MAX_BATCH_AUDIO_BYTES = 10 * 1024 * 1024;
var MAX_QUEUED_AUDIO_BYTES = MAX_BATCH_AUDIO_BYTES * 2;
var MAX_CONNECTION_MS = 6 * 6e4;
var MAX_CONCURRENT_VOICE_SESSIONS = 8;
var GENERIC_TRANSCRIPTION_ERROR = "Voice transcription failed. Please try again.";
var NO_VOICE_MODEL_ERROR = "No voice model is configured for this workspace.";
var SAMPLE_RATE = 16e3;
function encodeWav(pcm) {
  const header = Buffer.alloc(44);
  const dataLen = pcm.byteLength;
  header.write("RIFF", 0);
  header.writeUInt32LE(36 + dataLen, 4);
  header.write("WAVE", 8);
  header.write("fmt ", 12);
  header.writeUInt32LE(16, 16);
  header.writeUInt16LE(1, 20);
  header.writeUInt16LE(1, 22);
  header.writeUInt32LE(SAMPLE_RATE, 24);
  header.writeUInt32LE(SAMPLE_RATE * 2, 28);
  header.writeUInt16LE(2, 32);
  header.writeUInt16LE(16, 34);
  header.write("data", 36);
  header.writeUInt32LE(dataLen, 40);
  return Buffer.concat([header, Buffer.from(pcm)]);
}
__name(encodeWav, "encodeWav");
async function defaultOpenStream(ctx, callbacks) {
  try {
    const cfg = resolveVoiceStreamConfig({
      config: ctx.models,
      settings: ctx.settings,
      voiceModel: ctx.voiceModel
    });
    await assertVoiceBaseUrlNetworkAllowed(cfg);
    return await openVoiceStreamWithRetry(
      () => cfg.transport === "qwen-asr-realtime" ? openQwenAsrRealtimeStream(cfg, callbacks) : openVoiceStream(cfg, callbacks)
    );
  } catch (error2) {
    debugLogger.debug(`[voice-ws] stream open error: ${errMessage(error2)}`);
    throw new Error(GENERIC_TRANSCRIPTION_ERROR);
  }
}
__name(defaultOpenStream, "defaultOpenStream");
function defaultTranscribe(ctx, pcm) {
  return transcribeVoiceAudio(
    { data: encodeWav(pcm), mimeType: "audio/wav" },
    { config: ctx.models, settings: ctx.settings, voiceModel: ctx.voiceModel }
  ).catch((error2) => {
    debugLogger.debug(
      `[voice-ws] batch transcription error: ${errMessage(error2)}`
    );
    throw new Error(GENERIC_TRANSCRIPTION_ERROR);
  });
}
__name(defaultTranscribe, "defaultTranscribe");
function errMessage(error2) {
  return error2 instanceof Error ? error2.message : String(error2);
}
__name(errMessage, "errMessage");
function voiceConfigErrorMessage(error2) {
  const message = errMessage(error2);
  return message === NO_VOICE_MODEL_ERROR ? message : GENERIC_TRANSCRIPTION_ERROR;
}
__name(voiceConfigErrorMessage, "voiceConfigErrorMessage");
function toBuffer(data) {
  if (Buffer.isBuffer(data)) return data;
  if (Array.isArray(data)) return Buffer.concat(data);
  return Buffer.from(data);
}
__name(toBuffer, "toBuffer");
function parseControl(text) {
  let parsed;
  try {
    parsed = JSON.parse(text);
  } catch {
    return void 0;
  }
  const type = parsed?.type;
  if (type === "start" || type === "stop" || type === "abort") {
    return { type };
  }
  return void 0;
}
__name(parseControl, "parseControl");
function createVoiceWsConnectionHandler(boundWorkspace, deps = {}) {
  const loadContext = deps.loadContext ?? loadDaemonVoiceContext;
  const openStream = deps.openStream ?? defaultOpenStream;
  const transcribe = deps.transcribe ?? defaultTranscribe;
  let activeSessions = 0;
  return (ws) => {
    if (activeSessions >= MAX_CONCURRENT_VOICE_SESSIONS) {
      writeStderrLine(
        `qwen serve: voice websocket rejected; activeSessions=${activeSessions}`
      );
      try {
        ws.send(
          JSON.stringify({
            type: "error",
            message: "Too many voice sessions in progress; try again shortly."
          })
        );
        ws.close(1013, "busy");
      } catch {
      }
      return;
    }
    activeSessions++;
    writeStderrLine(
      `qwen serve: voice websocket accepted; activeSessions=${activeSessions}`
    );
    let released = false;
    const releaseSlot = /* @__PURE__ */ __name(() => {
      if (!released) {
        released = true;
        activeSessions--;
        writeStderrLine(
          `qwen serve: voice websocket slot released; activeSessions=${activeSessions}`
        );
      }
    }, "releaseSlot");
    let state = "idle";
    let ctx;
    let session;
    let sessionPromise;
    const pcmChunks = [];
    let bufferedBytes = 0;
    let queuedBytes = 0;
    let pendingOperations = 0;
    let chain = Promise.resolve();
    const hardTimer = setTimeout(() => {
      if (state !== "closed") fail("Voice session exceeded the time limit.");
    }, MAX_CONNECTION_MS);
    hardTimer.unref?.();
    const isClosed = /* @__PURE__ */ __name(() => state === "closed", "isClosed");
    const releaseSlotWhenIdle = /* @__PURE__ */ __name(() => {
      if (state === "closed" && pendingOperations === 0) releaseSlot();
    }, "releaseSlotWhenIdle");
    const sendJson = /* @__PURE__ */ __name((obj) => {
      if (ws.readyState === ws.OPEN) {
        try {
          ws.send(JSON.stringify(obj));
        } catch {
        }
      }
    }, "sendJson");
    function cleanup() {
      state = "closed";
      clearTimeout(hardTimer);
      if (session) {
        try {
          session.abort();
        } catch {
        }
        session = void 0;
      }
      sessionPromise = void 0;
      pcmChunks.length = 0;
      bufferedBytes = 0;
      queuedBytes = 0;
    }
    __name(cleanup, "cleanup");
    function fail(message) {
      if (state === "closed") return;
      writeStderrLine(`qwen serve: voice websocket failed: ${message}`);
      sendJson({ type: "error", message });
      cleanup();
      releaseSlotWhenIdle();
      try {
        ws.close(1011, "voice error");
      } catch {
      }
    }
    __name(fail, "fail");
    async function ensureStarted() {
      if (ctx) return;
      try {
        ctx = loadContext(boundWorkspace);
      } catch (error2) {
        debugLogger.debug(
          `[voice-ws] load context error: ${errMessage(error2)}`
        );
        fail(voiceConfigErrorMessage(error2));
        return;
      }
      sendJson({
        type: "ready",
        streaming: ctx.streaming,
        model: ctx.voiceModel
      });
      if (ctx.streaming) {
        const callbacks = {
          onInterim: /* @__PURE__ */ __name((text) => sendJson({ type: "interim", text }), "onInterim"),
          onError: /* @__PURE__ */ __name((error2) => {
            debugLogger.debug(
              `[voice-ws] upstream error: ${errMessage(error2)}`
            );
            fail(GENERIC_TRANSCRIPTION_ERROR);
          }, "onError")
        };
        const opening = openStream(ctx, callbacks);
        sessionPromise = opening;
        const opened = await opening;
        if (state === "closed") {
          try {
            opened.abort();
          } catch {
          }
          return;
        }
        session = opened;
      }
      if (state === "idle") state = "active";
    }
    __name(ensureStarted, "ensureStarted");
    async function finalize() {
      if (state === "closed" || state === "finalizing") return;
      state = "finalizing";
      await ensureStarted();
      if (isClosed()) return;
      let transcript = "";
      if (ctx.streaming) {
        const active = session ?? (sessionPromise ? await sessionPromise : void 0);
        if (isClosed()) return;
        if (active) {
          try {
            transcript = await active.finish();
          } finally {
            session = void 0;
          }
        }
      } else if (pcmChunks.length > 0) {
        transcript = await transcribe(ctx, Buffer.concat(pcmChunks));
      }
      sendJson({ type: "final", text: transcript });
      writeStderrLine("qwen serve: voice websocket finalized successfully");
      cleanup();
      try {
        ws.close(1e3, "done");
      } catch {
      }
    }
    __name(finalize, "finalize");
    async function handleMessage(data, isBinary) {
      if (state === "closed" || state === "finalizing") return;
      if (isBinary) {
        await ensureStarted();
        if (isClosed()) return;
        if (ctx.streaming) {
          const active = session ?? (sessionPromise ? await sessionPromise : void 0);
          active?.pushAudio(data);
        } else {
          bufferedBytes += data.byteLength;
          if (bufferedBytes > MAX_BATCH_AUDIO_BYTES) {
            fail("Recording is too long for transcription (max ~5 minutes).");
            return;
          }
          pcmChunks.push(data);
        }
        return;
      }
      const control = parseControl(data.toString("utf8"));
      if (!control) return;
      switch (control.type) {
        case "start":
          writeStderrLine("qwen serve: voice websocket start received");
          await ensureStarted();
          return;
        case "stop":
          writeStderrLine("qwen serve: voice websocket stop received");
          await finalize();
          return;
        default:
          return;
      }
    }
    __name(handleMessage, "handleMessage");
    ws.on("message", (data, isBinary) => {
      const buf = toBuffer(data);
      if (!isBinary) {
        const control = parseControl(buf.toString("utf8"));
        if (control?.type === "abort") {
          writeStderrLine("qwen serve: voice websocket abort received");
          cleanup();
          try {
            ws.close(1e3, "aborted");
          } catch {
          }
          releaseSlotWhenIdle();
          return;
        }
      }
      const queuedSize = isBinary ? buf.byteLength : 0;
      if (queuedSize > 0) {
        queuedBytes += queuedSize;
        if (queuedBytes > MAX_QUEUED_AUDIO_BYTES) {
          fail("Queued voice audio exceeded the memory limit.");
          releaseSlotWhenIdle();
          return;
        }
      }
      chain = chain.then(async () => {
        pendingOperations++;
        try {
          await handleMessage(buf, isBinary);
        } finally {
          if (queuedSize > 0) {
            queuedBytes = Math.max(0, queuedBytes - queuedSize);
          }
          pendingOperations--;
          releaseSlotWhenIdle();
        }
      }).catch((error2) => {
        debugLogger.debug(`[voice-ws] ${errMessage(error2)}`);
        fail(GENERIC_TRANSCRIPTION_ERROR);
        releaseSlotWhenIdle();
      });
    });
    ws.on("close", () => {
      if (state !== "closed") cleanup();
      releaseSlotWhenIdle();
    });
    ws.on("error", (error2) => {
      debugLogger.debug(`[voice-ws] socket error: ${error2.message}`);
      if (state !== "closed") cleanup();
      releaseSlotWhenIdle();
    });
  };
}
__name(createVoiceWsConnectionHandler, "createVoiceWsConnectionHandler");

// packages/cli/src/serve/cdp-tunnel/cdp-tunnel-registry.ts
init_esbuild_shims();
var CdpTunnelRegistry = class {
  static {
    __name(this, "CdpTunnelRegistry");
  }
  active;
  /**
   * Register (or replace) the active extension bridge. Returns an unregister
   * callback the `/acp` WS layer calls on socket close. Last-writer-wins: a new
   * extension connection supersedes the previous bridge.
   */
  register(endpoint) {
    const previous = this.active;
    if (previous && previous !== endpoint) {
      writeStderrLine(
        `qwen serve: /cdp tunnel \u2014 extension bridge '${endpoint.connectionId}' superseded the stale '${previous.connectionId}'`
      );
      previous.onExtensionGone?.();
    }
    this.active = endpoint;
    let unregistered = false;
    return () => {
      if (unregistered) return;
      unregistered = true;
      if (this.active === endpoint) this.active = void 0;
      endpoint.onExtensionGone?.();
    };
  }
  /** The active extension bridge, if any. */
  getActive() {
    return this.active;
  }
  /** Whether an extension bridge is currently registered. */
  hasActive() {
    return this.active !== void 0;
  }
  /**
   * Route an inbound `cdp_*` frame (from the extension `/acp` socket) to the
   * active bridge's bound reverse link. Returns true if consumed.
   */
  routeInbound(frame) {
    return this.active ? this.active.routeInbound(frame) : false;
  }
};

// packages/cli/src/serve/web-shell-static.ts
init_esbuild_shims();
var import_express2 = __toESM(require_express(), 1);
import * as path10 from "node:path";
var WEB_SHELL_CSP_DIRECTIVES = [
  "default-src 'self'",
  "script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'",
  "style-src 'self' 'unsafe-inline'",
  "font-src 'self' data:",
  "img-src 'self' data: blob:",
  "connect-src 'self'",
  "worker-src 'self' blob:",
  // base-uri does NOT fall back to default-src; lock it so an injected <base>
  // (the SPA renders AI-generated markdown) cannot repoint relative URLs to an
  // attacker origin.
  "base-uri 'none'"
];
function buildWebShellCsp(frameAncestors = []) {
  const fa = frameAncestors.length ? `frame-ancestors ${frameAncestors.join(" ")}` : "frame-ancestors 'none'";
  return [...WEB_SHELL_CSP_DIRECTIVES, fa].join("; ");
}
__name(buildWebShellCsp, "buildWebShellCsp");
var WEB_SHELL_CSP = buildWebShellCsp();
function isDocumentNavigation(req) {
  const fetchMode = req.headers["sec-fetch-mode"];
  const fetchDest = req.headers["sec-fetch-dest"];
  const accept = req.headers.accept ?? "";
  return fetchMode === "navigate" || fetchDest === "document" || accept.trim().toLowerCase().startsWith("text/html");
}
__name(isDocumentNavigation, "isDocumentNavigation");
function createSendIndex(webShellDir, frameAncestors = []) {
  const indexPath = path10.join(webShellDir, "index.html");
  const csp = buildWebShellCsp(frameAncestors);
  return (res) => {
    res.status(200).set("Content-Security-Policy", csp).set("X-Content-Type-Options", "nosniff").set("Referrer-Policy", "no-referrer").set(
      // `microphone=(self)` lets the same-origin Web Shell document request
      // the mic for voice dictation (the prompt won't even appear under an
      // empty `microphone=()` allowlist). Still blocks cross-origin iframes;
      // camera/geolocation/payment stay disabled (unused).
      "Permissions-Policy",
      "camera=(), microphone=(self), geolocation=(), payment=()"
    ).set("Cache-Control", "no-cache");
    if (frameAncestors.length === 0) {
      res.set("X-Frame-Options", "DENY");
    }
    res.sendFile(
      indexPath,
      { cacheControl: false, dotfiles: "allow" },
      (err) => {
        if (!err) return;
        writeStderrLine(
          `qwen serve: Web Shell index send failed: ${err instanceof Error ? err.message : String(err)}`
        );
        if (!res.headersSent) {
          res.status(500).type("text/plain").send("Failed to load Web Shell");
        } else {
          res.end();
        }
      }
    );
  };
}
__name(createSendIndex, "createSendIndex");
function mountWebShellAssets(app, webShellDir, frameAncestors = []) {
  const sendIndex = createSendIndex(webShellDir, frameAncestors);
  app.use(
    "/assets",
    import_express2.default.static(path10.join(webShellDir, "assets"), {
      index: false,
      immutable: true,
      maxAge: "1y"
    })
  );
  app.use("/assets", (req, res) => {
    if (isServeDebugMode()) {
      writeStderrLine(
        `qwen serve: Web Shell asset not found: ${req.originalUrl}`
      );
    }
    res.status(404).type("text/plain").send("Not found");
  });
  app.get("/", (_req, res) => sendIndex(res));
}
__name(mountWebShellAssets, "mountWebShellAssets");
function mountWebShellSpaFallback(app, webShellDir, frameAncestors = []) {
  const sendIndex = createSendIndex(webShellDir, frameAncestors);
  app.use((req, res, next) => {
    if (req.method !== "GET" && req.method !== "HEAD") return next();
    if (!isDocumentNavigation(req)) return next();
    if (isServeDebugMode()) {
      writeStderrLine(
        `qwen serve: Web Shell SPA fallback served for ${req.method} ${req.originalUrl}`
      );
    }
    sendIndex(res);
  });
}
__name(mountWebShellSpaFallback, "mountWebShellSpaFallback");

// packages/cli/src/serve/workspace-remember.ts
init_esbuild_shims();
import { randomUUID as randomUUID2 } from "node:crypto";
var debugLogger2 = createDebugLogger("WORKSPACE_REMEMBER");
function nowIso() {
  return (/* @__PURE__ */ new Date()).toISOString();
}
__name(nowIso, "nowIso");
function createRememberTaskId() {
  return `remember-${randomUUID2()}`;
}
__name(createRememberTaskId, "createRememberTaskId");
function cloneTask(task) {
  return {
    taskId: task.taskId,
    status: task.status,
    contextMode: task.contextMode,
    createdAt: task.createdAt,
    updatedAt: task.updatedAt,
    result: task.result ? {
      ...task.result,
      filesTouched: [...task.result.filesTouched],
      touchedScopes: [...task.result.touchedScopes]
    } : void 0,
    error: task.error ? { ...task.error } : void 0
  };
}
__name(cloneTask, "cloneTask");
function publicErrorMessage(code) {
  if (code === "managed_memory_unavailable") {
    return "Managed memory is unavailable for this daemon workspace.";
  }
  if (code === "remember_path_escape") {
    return "Remember agent touched a path outside managed memory.";
  }
  if (code === "remember_queue_full") {
    return "Workspace memory remember queue is full.";
  }
  if (code === "remember_timeout") {
    return "Workspace memory remember timed out.";
  }
  return "Workspace memory remember failed.";
}
__name(publicErrorMessage, "publicErrorMessage");
function publicErrorStatus(code) {
  if (code === "remember_queue_full") return 429;
  if (code === "managed_memory_unavailable") return 409;
  return 500;
}
__name(publicErrorStatus, "publicErrorStatus");
var WorkspaceRememberTaskLane = class _WorkspaceRememberTaskLane {
  constructor(bridge) {
    this.bridge = bridge;
  }
  static {
    __name(this, "WorkspaceRememberTaskLane");
  }
  static MAX_TASKS = 1e3;
  static MAX_PENDING = 16;
  tasks = /* @__PURE__ */ new Map();
  tail = Promise.resolve();
  pendingCount() {
    let count = 0;
    for (const task of this.tasks.values()) {
      if (task.status === "queued" || task.status === "running") count++;
    }
    return count;
  }
  evictTerminalTasks() {
    if (this.tasks.size <= _WorkspaceRememberTaskLane.MAX_TASKS) return;
    for (const [id, task] of this.tasks) {
      if (task.status === "completed" || task.status === "failed") {
        this.tasks.delete(id);
        if (this.tasks.size <= _WorkspaceRememberTaskLane.MAX_TASKS) break;
      }
    }
  }
  enqueue(params) {
    if (this.pendingCount() >= _WorkspaceRememberTaskLane.MAX_PENDING) {
      throw Object.assign(new Error("Remember queue is full"), {
        code: "remember_queue_full"
      });
    }
    const task = {
      taskId: createRememberTaskId(),
      status: "queued",
      contextMode: params.contextMode,
      createdAt: nowIso(),
      updatedAt: nowIso(),
      ...params.originatorClientId ? { originatorClientId: params.originatorClientId } : {}
    };
    this.tasks.set(task.taskId, task);
    this.evictTerminalTasks();
    const run = /* @__PURE__ */ __name(async () => {
      task.status = "running";
      task.updatedAt = nowIso();
      try {
        const result = await this.bridge.runWorkspaceMemoryRemember({
          content: params.content,
          contextMode: params.contextMode
        });
        task.status = "completed";
        task.result = {
          summary: result.filesTouched.length > 0 ? "Memory update completed." : "No memory files updated.",
          filesTouched: result.filesTouched,
          touchedScopes: result.touchedScopes
        };
        task.updatedAt = nowIso();
      } catch (err) {
        const code = extractRememberErrorCode(err);
        debugLogger2.error("Remember task failed:", err);
        task.status = "failed";
        task.error = {
          code,
          message: publicErrorMessage(code)
        };
        task.updatedAt = nowIso();
      } finally {
        this.evictTerminalTasks();
      }
      if (task.status === "completed" && task.result && task.result.touchedScopes.length > 0) {
        try {
          this.bridge.publishWorkspaceEvent({
            type: "memory_changed",
            data: {
              scope: "managed",
              source: "workspace_memory_remember",
              taskId: task.taskId,
              touchedScopes: task.result.touchedScopes
            },
            ...params.originatorClientId ? { originatorClientId: params.originatorClientId } : {}
          });
        } catch (err) {
          debugLogger2.error("Failed to publish memory_changed event:", err);
        }
      }
    }, "run");
    this.tail = this.tail.then(run, run);
    void this.tail.catch((err) => {
      debugLogger2.error("Unhandled task lane error:", err);
    });
    return cloneTask(task);
  }
  get(taskId, requesterClientId) {
    const task = this.tasks.get(taskId);
    if (!task) return void 0;
    if (task.originatorClientId) {
      if (task.originatorClientId !== requesterClientId) return void 0;
    } else if (requesterClientId) {
      return void 0;
    }
    return cloneTask(task);
  }
};
function validateOriginatorClientId(deps, req, res) {
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return null;
  if (clientId === void 0) return void 0;
  const known = deps.bridge.knownClientIds();
  if (!known.has(clientId)) {
    res.status(400).json({
      error: `Client id "${clientId}" is not registered for this workspace`,
      code: "invalid_client_id",
      clientId
    });
    return null;
  }
  return clientId;
}
__name(validateOriginatorClientId, "validateOriginatorClientId");
function mountWorkspaceMemoryRememberRoutes(app, deps) {
  app.post(
    "/workspace/memory/remember",
    deps.mutate({ strict: true }),
    async (req, res) => {
      const body = deps.safeBody(req);
      const content = body["content"];
      const trimmedContent = typeof content === "string" ? content.trim() : "";
      if (!trimmedContent) {
        res.status(400).json({
          error: "`content` must be a non-empty string",
          code: "invalid_content"
        });
        return;
      }
      if (Buffer.byteLength(trimmedContent, "utf8") > MAX_REMEMBER_CONTENT_BYTES) {
        res.status(400).json({
          error: `\`content\` exceeds the ${MAX_REMEMBER_CONTENT_BYTES}-byte limit`,
          code: "invalid_content"
        });
        return;
      }
      const contextModeRaw = body["contextMode"] ?? "workspace";
      if (contextModeRaw !== "workspace" && contextModeRaw !== "clean") {
        res.status(400).json({
          error: '`contextMode` must be "workspace", "clean", or omitted',
          code: "invalid_context_mode"
        });
        return;
      }
      const originatorClientId = validateOriginatorClientId(deps, req, res);
      if (originatorClientId === null) return;
      try {
        const available = await deps.bridge.isWorkspaceMemoryRememberAvailable();
        if (!available) {
          res.status(409).json({
            error: "Managed memory is unavailable for this daemon workspace",
            code: "managed_memory_unavailable"
          });
          return;
        }
      } catch (err) {
        debugLogger2.error("Availability check failed:", err);
        res.status(500).json({
          error: "Workspace memory remember failed.",
          code: "remember_failed"
        });
        return;
      }
      let task;
      try {
        task = deps.lane.enqueue({
          content: trimmedContent,
          contextMode: contextModeRaw,
          ...originatorClientId ? { originatorClientId } : {}
        });
      } catch (err) {
        const code = extractRememberErrorCode(err);
        res.status(publicErrorStatus(code)).json({
          error: publicErrorMessage(code),
          code
        });
        return;
      }
      res.status(202).json(task);
    }
  );
  app.get(
    "/workspace/memory/remember/:taskId",
    deps.mutate({ strict: true }),
    (req, res) => {
      const requesterClientId = validateOriginatorClientId(deps, req, res);
      if (requesterClientId === null) return;
      const task = deps.lane.get(req.params["taskId"], requesterClientId);
      if (!task) {
        res.status(404).json({
          error: "Workspace memory remember task not found",
          code: "remember_task_not_found"
        });
        return;
      }
      res.status(200).json(task);
    }
  );
}
__name(mountWorkspaceMemoryRememberRoutes, "mountWorkspaceMemoryRememberRoutes");

// packages/cli/src/serve/routes/daemon-status.ts
init_esbuild_shims();
function registerDaemonStatusRoutes(app, deps) {
  app.get("/daemon/status", async (req, res) => {
    const detail = parseDaemonStatusDetail(req.query["detail"]);
    if (!detail.ok || !detail.detail) {
      res.status(400).json({
        error: "detail must be one of: summary, full",
        code: "invalid_detail"
      });
      return;
    }
    try {
      res.status(200).json(
        await buildDaemonStatusResponse(detail.detail, {
          opts: deps.opts,
          boundWorkspace: deps.boundWorkspace,
          bridge: deps.bridge,
          workspace: deps.workspace,
          daemonLog: deps.daemonLog,
          startup: deps.startup,
          qwenCodeVersion: deps.qwenCodeVersion,
          acpHandle: deps.getAcpHandle(),
          rateLimiter: deps.getRateLimiter(),
          getRestSseActive: deps.getRestSseActive,
          features: deps.currentServeFeatures(),
          protocolVersions: getServeProtocolVersions(),
          supportedDeviceFlowProviders: deps.getSupportedDeviceFlowProviders(),
          deviceFlowRegistry: deps.deviceFlowRegistry,
          sessionShellCommandEnabled: deps.sessionShellCommandEnabled,
          getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot
        })
      );
    } catch (err) {
      writeStderrLine(
        `qwen serve: /daemon/status failed: ${err instanceof Error ? err.message : String(err)}`
      );
      res.status(500).json({
        error: "Failed to build daemon status",
        code: "daemon_status_failed"
      });
    }
  });
}
__name(registerDaemonStatusRoutes, "registerDaemonStatusRoutes");

// packages/cli/src/serve/routes/health-demo.ts
init_esbuild_shims();

// packages/cli/src/serve/demo.ts
init_esbuild_shims();
function getDemoHtml(_port) {
  return (
    /* html */
    `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Qwen Serve Demo</title>
<style>
  :root { --bg: #1a1a2e; --surface: #16213e; --border: #0f3460; --accent: #e94560; --text: #eee; --text2: #aab; --ok: #4ade80; --warn: #fbbf24; --err: #f87171; }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { font-family: 'SF Mono', 'Menlo', 'Consolas', monospace; font-size: 13px; background: var(--bg); color: var(--text); min-height: 100vh; }
  .header { background: var(--surface); border-bottom: 1px solid var(--border); padding: 12px 20px; display: flex; align-items: center; gap: 12px; }
  .header h1 { font-size: 16px; font-weight: 600; }
  .header .badge { font-size: 11px; background: var(--accent); color: #fff; padding: 2px 8px; border-radius: 10px; }
  .header .status { margin-left: auto; display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text2); }
  .header .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--err); }
  .header .dot.ok { background: var(--ok); }
  .container { display: grid; grid-template-columns: 340px 1fr; height: calc(100vh - 49px); }
  .sidebar { background: var(--surface); border-right: 1px solid var(--border); overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 12px; }
  .panel { background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 12px; }
  .panel h3 { font-size: 12px; color: var(--accent); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
  .panel label { display: block; font-size: 11px; color: var(--text2); margin-bottom: 3px; }
  .panel input, .panel textarea { width: 100%; background: var(--surface); border: 1px solid var(--border); color: var(--text); padding: 6px 8px; border-radius: 4px; font-family: inherit; font-size: 12px; margin-bottom: 8px; }
  .panel input:focus, .panel textarea:focus { outline: none; border-color: var(--accent); }
  .panel textarea { resize: vertical; min-height: 60px; }
  .btn { display: inline-flex; align-items: center; gap: 4px; padding: 6px 14px; border: 1px solid var(--border); border-radius: 4px; background: var(--surface); color: var(--text); font-family: inherit; font-size: 12px; cursor: pointer; transition: all 0.15s; }
  .btn:hover { border-color: var(--accent); color: var(--accent); }
  .btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
  .btn.primary:hover { opacity: 0.85; }
  .btn:disabled { opacity: 0.4; cursor: not-allowed; }
  .btn-row { display: flex; gap: 6px; flex-wrap: wrap; }
  .main { display: flex; flex-direction: column; overflow: hidden; }
  .tabs { display: flex; background: var(--surface); border-bottom: 1px solid var(--border); }
  .tab { padding: 8px 16px; font-size: 12px; color: var(--text2); cursor: pointer; border-bottom: 2px solid transparent; }
  .tab.active { color: var(--accent); border-bottom-color: var(--accent); }
  .tab-content { flex: 1; overflow: hidden; display: none; flex-direction: column; }
  .tab-content.active { display: flex; }
  .log { flex: 1; overflow-y: auto; padding: 12px; font-size: 12px; line-height: 1.6; }
  .log-entry { padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,0.03); word-break: break-all; }
  .log-entry .ts { color: var(--text2); margin-right: 8px; font-size: 11px; }
  .log-entry .tag { display: inline-block; padding: 0 5px; border-radius: 3px; font-size: 10px; margin-right: 6px; }
  .tag-info { background: #1e40af; color: #93c5fd; }
  .tag-event { background: #065f46; color: #6ee7b7; }
  .tag-error { background: #7f1d1d; color: #fca5a5; }
  .tag-response { background: #713f12; color: #fde68a; }
  .tag-thought { background: #4c1d95; color: #c4b5fd; }
  .tag-message { background: #164e63; color: #67e8f9; }
  .chat { flex: 1; overflow-y: auto; padding: 16px; }
  .chat-msg { margin-bottom: 12px; max-width: 85%; }
  .chat-msg.user { margin-left: auto; }
  .chat-msg .bubble { padding: 8px 12px; border-radius: 8px; line-height: 1.5; white-space: pre-wrap; }
  .chat-msg.user .bubble { background: var(--accent); color: #fff; border-bottom-right-radius: 2px; }
  .chat-msg.assistant .bubble { background: var(--surface); border: 1px solid var(--border); border-bottom-left-radius: 2px; }
  .chat-msg .meta { font-size: 10px; color: var(--text2); margin-top: 3px; }
  .chat-input { display: flex; gap: 8px; padding: 12px; border-top: 1px solid var(--border); background: var(--surface); }
  .chat-input input { flex: 1; }
  .session-info { font-size: 11px; color: var(--text2); padding: 4px 0; }
  .session-info span { color: var(--ok); }
  .empty-hint { color: var(--text2); text-align: center; padding: 40px 20px; font-size: 12px; }
  .permission-card { background: var(--surface); border: 1px solid var(--warn); border-radius: 6px; padding: 10px 12px; margin: 8px 0; }
  .permission-card h4 { color: var(--warn); font-size: 12px; margin-bottom: 6px; }
  .permission-card .opt-btn { margin-right: 4px; margin-top: 4px; }
</style>
</head>
<body>

<div class="header">
  <h1>Qwen Serve</h1>
  <span class="badge">Demo</span>
  <div class="status">
    <span class="dot" id="statusDot"></span>
    <span id="statusText">Connecting...</span>
  </div>
</div>

<div class="container">
  <div class="sidebar">
    <div class="panel">
      <h3>Session</h3>
      <label>Working Directory (cwd)</label>
      <input type="text" id="cwdInput" placeholder="/path/to/project" />
      <div class="btn-row">
        <button class="btn primary" id="btnCreateSession">Create / Attach</button>
        <button class="btn" id="btnListSessions">List</button>
      </div>
      <div class="session-info" id="sessionInfo" style="display:none">
        Session: <span id="sessionIdDisplay"></span>
      </div>
    </div>

    <div class="panel">
      <h3>Prompt</h3>
      <textarea id="promptInput" placeholder="Type your prompt here..."></textarea>
      <div class="btn-row">
        <button class="btn primary" id="btnSendPrompt" disabled>Send</button>
        <button class="btn" id="btnCancel" disabled>Cancel</button>
      </div>
    </div>

    <div class="panel">
      <h3>Model</h3>
      <input type="text" id="modelInput" placeholder="Model ID (e.g. qwen3-coder)" />
      <button class="btn" id="btnSetModel" disabled>Switch Model</button>
    </div>

    <div class="panel">
      <h3>Auth Token</h3>
      <label>Bearer Token (if --token is set)</label>
      <input type="password" id="tokenInput" placeholder="Optional bearer token" />
    </div>

    <div class="panel">
      <h3>Quick Actions</h3>
      <div class="btn-row">
        <button class="btn" id="btnHealth">Health</button>
        <button class="btn" id="btnHealthDeep">Health (deep)</button>
        <button class="btn" id="btnCaps">Capabilities</button>
      </div>
    </div>

    <div class="panel" id="permissionPanel" style="display:none">
      <h3>Pending Permissions</h3>
      <div id="permissionList"></div>
    </div>
  </div>

  <div class="main">
    <div class="tabs">
      <div class="tab active" data-tab="chat">Chat</div>
      <div class="tab" data-tab="events">Events</div>
      <div class="tab" data-tab="requests">API Log</div>
    </div>

    <div class="tab-content active" id="tab-chat">
      <div class="chat" id="chatArea">
        <div class="empty-hint">Create a session to start chatting</div>
      </div>
      <div class="chat-input">
        <input type="text" id="chatInput" placeholder="Send a message..." disabled />
        <button class="btn primary" id="btnChatSend" disabled>Send</button>
      </div>
    </div>

    <div class="tab-content" id="tab-events">
      <div class="log" id="eventLog"></div>
    </div>

    <div class="tab-content" id="tab-requests">
      <div class="log" id="requestLog"></div>
    </div>
  </div>
</div>

<script>
(function() {
  const BASE = '';
  let sessionId = null;
  let eventSource = null;
  let currentAssistantBubble = null;
  let currentThought = '';

  // --- DOM refs ---
  const $ = (s) => document.querySelector(s);
  const statusDot = $('#statusDot');
  const statusText = $('#statusText');
  const cwdInput = $('#cwdInput');
  const promptInput = $('#promptInput');
  const modelInput = $('#modelInput');
  const tokenInput = $('#tokenInput');
  const chatArea = $('#chatArea');
  const chatInput = $('#chatInput');
  const eventLog = $('#eventLog');
  const requestLog = $('#requestLog');
  const sessionInfo = $('#sessionInfo');
  const sessionIdDisplay = $('#sessionIdDisplay');
  const permissionPanel = $('#permissionPanel');
  const permissionList = $('#permissionList');

  // --- Tabs ---
  document.querySelectorAll('.tab').forEach(tab => {
    tab.addEventListener('click', () => {
      document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
      document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
      tab.classList.add('active');
      $('#tab-' + tab.dataset.tab).classList.add('active');
    });
  });

  // --- Logging ---
  function ts() {
    return new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
  }
  const MAX_LOG_ENTRIES = 500;
  function addLog(container, tag, tagClass, msg) {
    const el = document.createElement('div');
    el.className = 'log-entry';
    const text = typeof msg === 'object' ? JSON.stringify(msg, null, 2) : msg;
    const tsSpan = document.createElement('span');
    tsSpan.className = 'ts';
    tsSpan.textContent = ts();
    const tagSpan = document.createElement('span');
    tagSpan.className = 'tag ' + tagClass;
    tagSpan.textContent = tag;
    el.appendChild(tsSpan);
    el.appendChild(tagSpan);
    el.appendChild(document.createTextNode(text));
    container.appendChild(el);
    while (container.children.length > MAX_LOG_ENTRIES) {
      container.removeChild(container.firstChild);
    }
    container.scrollTop = container.scrollHeight;
  }
  function logEvent(tag, msg) { addLog(eventLog, tag, 'tag-event', msg); }
  function logRequest(tag, msg) { addLog(requestLog, tag, 'tag-info', msg); }
  function logResponse(tag, msg) { addLog(requestLog, tag, 'tag-response', msg); }
  function logError(tag, msg) { addLog(requestLog, tag, 'tag-error', msg); }

  function escHtml(s) {
    const d = document.createElement('div');
    d.textContent = String(s);
    return d.innerHTML;
  }

  // --- 401 token hint ---
  let tokenHintTimer = null;
  function highlightTokenInput(msg) {
    tokenInput.style.borderColor = 'var(--warn)';
    tokenInput.focus();
    // Show hint text below the input
    let hint = document.getElementById('tokenHint');
    if (!hint) {
      hint = document.createElement('div');
      hint.id = 'tokenHint';
      hint.style.cssText = 'font-size:11px;color:var(--warn);margin-top:-4px;margin-bottom:4px;';
      tokenInput.parentNode.insertBefore(hint, tokenInput.nextSibling);
    }
    hint.textContent = msg;
    if (tokenHintTimer) clearTimeout(tokenHintTimer);
    tokenHintTimer = setTimeout(() => {
      tokenInput.style.borderColor = '';
      hint.textContent = '';
    }, 6000);
  }

  // --- API helpers ---
  function authHeaders() {
    const token = tokenInput.value.trim();
    return token ? { 'Authorization': 'Bearer ' + token } : {};
  }

  async function api(method, path, body) {
    const opts = { method, headers: { ...authHeaders() } };
    if (body !== undefined) {
      opts.headers['Content-Type'] = 'application/json';
      opts.body = JSON.stringify(body);
    }
    logRequest(method, path + (body ? ' ' + JSON.stringify(body) : ''));
    try {
      const res = await fetch(BASE + path, opts);
      const text = await res.text();
      let data;
      try { data = JSON.parse(text); } catch { data = text; }
      if (!res.ok) {
        logError(res.status, JSON.stringify(data));
        if (res.status === 401) {
          highlightTokenInput('API returned 401 \u2014 enter your bearer token below');
        }
        return { ok: false, status: res.status, data };
      }
      logResponse(res.status, JSON.stringify(data));
      return { ok: true, status: res.status, data };
    } catch (err) {
      logError('ERR', err.message);
      return { ok: false, status: 0, data: { error: err.message } };
    }
  }

  // --- Health check on load ---
  async function checkHealth() {
    const r = await api('GET', '/health');
    if (r.ok) {
      statusDot.classList.add('ok');
      statusText.textContent = 'Connected';
    } else {
      statusDot.classList.remove('ok');
      statusText.textContent = 'Disconnected';
    }
  }

  // --- Session ---
  async function createSession() {
    const cwd = cwdInput.value.trim();
    if (!cwd) { alert('Please enter a working directory'); return; }
    const r = await api('POST', '/session', { cwd });
    if (r.ok) {
      sessionId = r.data.sessionId;
      sessionInfo.style.display = 'block';
      sessionIdDisplay.textContent = sessionId;
      enablePrompt(true);
      chatArea.innerHTML = '';
      currentAssistantBubble = null;
      pendingPerms.clear();
      renderPermissions();
      if (r.data.attached) {
        addChatMeta('Attached to existing session');
      } else {
        addChatMeta('New session created');
      }
      connectSSE();
    } else {
      const msg = r.data.error || 'session creation failed (' + r.status + ')';
      addChatMeta('Error: ' + msg);
      if (r.status === 401) highlightTokenInput('Token required or invalid');
    }
  }

  function enablePrompt(on) {
    $('#btnSendPrompt').disabled = !on;
    $('#btnCancel').disabled = !on;
    $('#btnSetModel').disabled = !on;
    chatInput.disabled = !on;
    $('#btnChatSend').disabled = !on;
  }

  // --- SSE ---
  function connectSSE() {
    if (eventSource) { eventSource.close(); }
    const url = BASE + '/session/' + sessionId + '/events';
    logEvent('SSE', 'Connecting to ' + url);

    // Use fetch-based SSE to avoid Origin header issues with EventSource
    const abort = new AbortController();
    eventSource = { close: () => abort.abort() };

    (async function readSSE() {
      try {
        const hdrs = authHeaders();
        const res = await fetch(url, { signal: abort.signal, headers: hdrs });
        if (!res.ok) {
          logEvent('SSE-ERR', 'HTTP ' + res.status);
          statusDot.classList.remove('ok');
          statusText.textContent = 'SSE failed (' + res.status + ')';
          enablePrompt(false);
          if (res.status === 401) {
            highlightTokenInput('SSE returned 401 \u2014 enter your bearer token and recreate the session');
          }
          return;
        }
        const reader = res.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';
        let currentEvent = {};

        while (true) {
          const { done, value } = await reader.read();
          if (done) break;
          buffer += decoder.decode(value, { stream: true });

          const lines = buffer.split('\\n');
          buffer = lines.pop();

          for (const line of lines) {
            if (line.startsWith('data: ')) {
              currentEvent.data = line.slice(6);
            } else if (line.startsWith('event: ')) {
              currentEvent.event = line.slice(7);
            } else if (line.startsWith('id: ')) {
              currentEvent.id = line.slice(4);
            } else if (line === '') {
              if (currentEvent.data) {
                handleSSEMessage(currentEvent);
              }
              currentEvent = {};
            }
          }
        }
        // Process any remaining buffered data
        if (currentEvent.data) handleSSEMessage(currentEvent);
        statusDot.classList.remove('ok');
        statusText.textContent = 'SSE stream ended';
        enablePrompt(false);
        logEvent('SSE', 'Stream ended by server');
      } catch (err) {
        if (err.name !== 'AbortError') {
          logEvent('SSE-ERR', err.message);
          statusDot.classList.remove('ok');
          statusText.textContent = 'SSE error';
          enablePrompt(false);
        }
      }
    })();
  }

  function handleSSEMessage(msg) {
    let data;
    try { data = JSON.parse(msg.data); } catch { data = msg.data; }
    const type = data.type || msg.event || 'unknown';

    if (type === 'session_update' && data.data) {
      const update = data.data.update || data.data;
      const kind = update.sessionUpdate;

      if (kind === 'agent_message_chunk') {
        const text = update.content?.text || '';
        if (text) appendAssistantChunk(text);
        if (update._meta?.usage) {
          const u = update._meta.usage;
          addChatMeta('tokens: in=' + u.inputTokens + ' out=' + u.outputTokens + ' | ' + (update._meta.durationMs || 0) + 'ms');
          currentAssistantBubble = null;
        }
        logEvent('MSG', text || '(usage meta)');
      } else if (kind === 'agent_thought_chunk') {
        const text = update.content?.text || '';
        currentThought += text;
        logEvent('THINK', text);
      } else {
        logEvent(kind || type, JSON.stringify(update));
      }
    } else if (type === 'permission_request') {
      handlePermissionRequest(data);
      logEvent('PERM', JSON.stringify(data.data));
    } else if (type === 'permission_resolved') {
      logEvent('PERM-OK', JSON.stringify(data.data));
      removePermission(data.data?.requestId);
    } else if (type === 'session_died') {
      addChatMeta('Session died: ' + JSON.stringify(data.data));
      logEvent('DIED', JSON.stringify(data.data));
      enablePrompt(false);
    } else if (type === 'model_switched') {
      addChatMeta('Model switched: ' + JSON.stringify(data.data));
      logEvent('MODEL', JSON.stringify(data.data));
    } else {
      logEvent(type, JSON.stringify(data));
    }
  }

  // --- Chat UI ---
  function addUserMessage(text) {
    currentAssistantBubble = null;
    currentThought = '';
    const el = document.createElement('div');
    el.className = 'chat-msg user';
    el.innerHTML = '<div class="bubble">' + escHtml(text) + '</div>';
    chatArea.appendChild(el);
    chatArea.scrollTop = chatArea.scrollHeight;
  }

  function appendAssistantChunk(text) {
    if (!currentAssistantBubble) {
      if (currentThought) {
        const te = document.createElement('div');
        te.className = 'chat-msg assistant';
        te.innerHTML = '<div class="bubble" style="color:var(--text2);font-style:italic;border-color:var(--border)">Thinking: ' + escHtml(currentThought) + '</div>';
        chatArea.appendChild(te);
        currentThought = '';
      }
      const el = document.createElement('div');
      el.className = 'chat-msg assistant';
      el.innerHTML = '<div class="bubble"></div>';
      chatArea.appendChild(el);
      currentAssistantBubble = el.querySelector('.bubble');
    }
    currentAssistantBubble.textContent += text;
    chatArea.scrollTop = chatArea.scrollHeight;
  }

  function addChatMeta(text) {
    const el = document.createElement('div');
    el.className = 'chat-msg assistant';
    el.innerHTML = '<div class="meta">' + escHtml(text) + '</div>';
    chatArea.appendChild(el);
    chatArea.scrollTop = chatArea.scrollHeight;
  }

  // --- Prompt ---
  let promptInFlight = false;
  async function sendPrompt(text) {
    if (!sessionId || !text.trim() || promptInFlight) return;
    promptInFlight = true;
    enablePrompt(false);
    addUserMessage(text.trim());
    promptInput.value = '';
    chatInput.value = '';
    try {
      const r = await api('POST', '/session/' + sessionId + '/prompt', {
        prompt: [{ type: 'text', text: text.trim() }]
      });
      if (r.ok) {
        addChatMeta('stopReason: ' + (r.data.stopReason || 'unknown'));
      } else {
        addChatMeta('Error: ' + (r.data.error || 'prompt failed (' + r.status + ')'));
      }
    } finally {
      promptInFlight = false;
      if (sessionId) enablePrompt(true);
    }
  }

  // --- Permission handling ---
  const pendingPerms = new Map();

  function handlePermissionRequest(data) {
    const req = data.data;
    const requestId = req?.requestId;
    if (!requestId) return;
    pendingPerms.set(requestId, req);
    renderPermissions();

    addChatMeta('Permission requested: ' + requestId);
  }

  function removePermission(requestId) {
    pendingPerms.delete(requestId);
    renderPermissions();
  }

  function renderPermissions() {
    if (pendingPerms.size === 0) {
      permissionPanel.style.display = 'none';
      return;
    }
    permissionPanel.style.display = 'block';
    permissionList.innerHTML = '';
    for (const [id, req] of pendingPerms) {
      const card = document.createElement('div');
      card.className = 'permission-card';

      const h4 = document.createElement('h4');
      h4.textContent = req.tool?.name || 'Permission';
      card.appendChild(h4);

      const idDiv = document.createElement('div');
      idDiv.style.cssText = 'font-size:11px;color:var(--text2);margin-bottom:6px';
      idDiv.textContent = id;
      card.appendChild(idDiv);

      // Show toolCall context (command, input, path, etc.) when available
      const tc = req.toolCall || req.tool;
      if (tc) {
        const detail = document.createElement('pre');
        detail.style.cssText = 'font-size:11px;color:var(--text2);margin-bottom:6px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow-y:auto;background:var(--bg);padding:6px;border-radius:4px;border:1px solid var(--border)';
        const parts = [];
        if (tc.command) parts.push('command: ' + tc.command);
        if (tc.input) parts.push('input: ' + (typeof tc.input === 'string' ? tc.input : JSON.stringify(tc.input, null, 2)));
        if (tc.path) parts.push('path: ' + tc.path);
        if (tc.diff) parts.push('diff: ' + tc.diff);
        if (tc.serverUrl) parts.push('url: ' + tc.serverUrl);
        if (parts.length === 0 && tc.name) parts.push('tool: ' + tc.name);
        if (parts.length > 0) {
          detail.textContent = parts.join('\\n');
          card.appendChild(detail);
        }
      }

      function makePermBtn(reqId, optId, label, isCancel) {
        const btn = document.createElement('button');
        btn.className = 'btn opt-btn';
        btn.textContent = label;
        btn.dataset.req = reqId;
        if (isCancel) {
          btn.dataset.cancel = '1';
          btn.style.borderColor = 'var(--err)';
          btn.style.color = 'var(--err)';
        } else {
          btn.dataset.opt = optId;
        }
        btn.addEventListener('click', async () => {
          let outcome;
          if (isCancel) {
            outcome = { outcome: 'cancelled' };
          } else {
            outcome = { outcome: 'selected', optionId: optId };
          }
          const result = await api('POST', '/permission/' + reqId, { outcome });
          if (result.ok) {
            removePermission(reqId);
          } else {
            logError('PERM-ERR', 'Failed to resolve permission ' + reqId + ': ' + JSON.stringify(result.data));
          }
        });
        return btn;
      }

      if (req.options && Array.isArray(req.options)) {
        for (const opt of req.options) {
          card.appendChild(makePermBtn(id, opt.optionId, opt.name || opt.optionId, false));
        }
      }
      card.appendChild(makePermBtn(id, null, 'Cancel', true));
      permissionList.appendChild(card);
    }
  }

  // --- Button bindings ---
  $('#btnCreateSession').addEventListener('click', createSession);
  $('#btnListSessions').addEventListener('click', async () => {
    const cwd = cwdInput.value.trim();
    if (!cwd) { alert('Enter a cwd first'); return; }
    await api('GET', '/workspace/' + encodeURIComponent(cwd) + '/sessions');
  });
  $('#btnSendPrompt').addEventListener('click', () => sendPrompt(promptInput.value));
  $('#btnChatSend').addEventListener('click', () => sendPrompt(chatInput.value));
  chatInput.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendPrompt(chatInput.value); }
  });
  promptInput.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendPrompt(promptInput.value); }
  });
  $('#btnCancel').addEventListener('click', async () => {
    if (!sessionId) return;
    await api('POST', '/session/' + sessionId + '/cancel', {});
  });
  $('#btnSetModel').addEventListener('click', async () => {
    if (!sessionId) return;
    const mid = modelInput.value.trim();
    if (!mid) { alert('Enter a model ID'); return; }
    await api('POST', '/session/' + sessionId + '/model', { modelId: mid });
  });
  $('#btnHealth').addEventListener('click', () => api('GET', '/health'));
  $('#btnHealthDeep').addEventListener('click', () => api('GET', '/health?deep=1'));
  $('#btnCaps').addEventListener('click', () => api('GET', '/capabilities'));

  // --- Init ---
  checkHealth();
})();
</script>
</body>
</html>`
  );
}
__name(getDemoHtml, "getDemoHtml");

// packages/cli/src/serve/routes/health-demo.ts
function createHealthDemoRoutes(deps) {
  const { opts, getPort, bridge, getActiveSseCount: getActiveSseCount2, getRateLimiter } = deps;
  const demoHandler = /* @__PURE__ */ __name((_req, res) => {
    try {
      res.type("html").set("X-Frame-Options", "DENY").set(
        "Content-Security-Policy",
        "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'; frame-ancestors 'none'"
      ).send(getDemoHtml(getPort()));
    } catch (err) {
      writeStderrLine(
        `qwen serve: /demo render failed: ${err instanceof Error ? err.message : String(err)}`
      );
      res.status(500).json({ error: "Failed to render demo page" });
    }
  }, "demoHandler");
  const healthHandler = /* @__PURE__ */ __name((req, res) => {
    const deepQuery = req.query["deep"];
    const deep = deepQuery === "1" || deepQuery === "true" || deepQuery === "";
    if (!deep) {
      res.status(200).json({ status: "ok" });
      return;
    }
    try {
      const lastActivity = bridge.lastActivityAt;
      const now = Date.now();
      const rateLimiter = getRateLimiter();
      res.status(200).json({
        status: "ok",
        sessions: bridge.sessionCount,
        pendingPermissions: bridge.pendingPermissionCount,
        activePrompts: bridge.activePromptCount,
        connectedClients: getActiveSseCount2(),
        channelAlive: bridge.isChannelLive(),
        lastActivityAt: lastActivity !== null ? new Date(lastActivity).toISOString() : null,
        idleSinceMs: lastActivity !== null ? now - lastActivity : null,
        ...rateLimiter ? { rateLimitHits: rateLimiter.getHitCounts() } : {}
      });
    } catch (err) {
      writeStderrLine(
        `qwen serve: /health deep probe failed: ${err instanceof Error ? err.message : String(err)}`
      );
      res.status(503).json({ status: "degraded" });
    }
  }, "healthHandler");
  const loopback = isLoopbackBind(opts.hostname);
  const exposeHealthPreAuth = loopback && !opts.requireAuth;
  return {
    exposeHealthPreAuth,
    register(app) {
      app.get("/health", healthHandler);
      app.get("/demo", demoHandler);
    }
  };
}
__name(createHealthDemoRoutes, "createHealthDemoRoutes");

// packages/cli/src/serve/routes/workspace-auth.ts
init_esbuild_shims();

// packages/cli/src/serve/server/auth-provider-helpers.ts
init_esbuild_shims();
import * as net from "node:net";
var AUTH_PROVIDER_STEPS = [
  "protocol",
  "baseUrl",
  "apiKey",
  "models",
  "advancedConfig"
];
function buildAuthProviderDescriptor(provider) {
  const steps = AUTH_PROVIDER_STEPS.filter(
    (step) => shouldShowStep(provider, step)
  );
  return {
    id: provider.id,
    label: provider.label,
    description: provider.description,
    ...provider.uiGroup ? { uiGroup: provider.uiGroup } : {},
    protocol: provider.protocol,
    ...provider.protocolOptions ? { protocolOptions: [...provider.protocolOptions] } : {},
    ...provider.baseUrl !== void 0 ? { baseUrl: provider.baseUrl } : {},
    ...typeof provider.envKey === "string" ? { envKey: provider.envKey } : {},
    ...provider.models ? {
      models: provider.models.map((model) => ({
        id: model.id,
        ...model.contextWindowSize !== void 0 ? { contextWindowSize: model.contextWindowSize } : {},
        ...model.enableThinking !== void 0 ? { enableThinking: model.enableThinking } : {},
        ...model.modalities ? { modalities: model.modalities } : {},
        ...model.description ? { description: model.description } : {}
      }))
    } : {},
    ...provider.modelsEditable !== void 0 ? { modelsEditable: provider.modelsEditable } : {},
    ...provider.apiKeyPlaceholder ? { apiKeyPlaceholder: provider.apiKeyPlaceholder } : {},
    ...typeof provider.documentationUrl === "string" ? { documentationUrl: provider.documentationUrl } : {},
    ...provider.showAdvancedConfig !== void 0 ? { showAdvancedConfig: provider.showAdvancedConfig } : {},
    ...provider.uiLabels ? { uiLabels: provider.uiLabels } : {},
    steps
  };
}
__name(buildAuthProviderDescriptor, "buildAuthProviderDescriptor");
function buildAuthProviderCatalog(workspaceCwd) {
  const providers = ALL_PROVIDERS.map(buildAuthProviderDescriptor);
  const providerIdsByGroup = /* @__PURE__ */ __name((group) => providers.filter((provider) => provider.uiGroup === group).map((provider) => provider.id), "providerIdsByGroup");
  return {
    v: 1,
    workspaceCwd,
    providers,
    groups: [
      {
        id: "alibaba",
        label: "Alibaba ModelStudio",
        description: "Official recommended setup: Coding Plan, Token Plan, or Standard API Key",
        providerIds: providerIdsByGroup("alibaba")
      },
      {
        id: "third-party",
        label: "Third-party Providers",
        description: "Choose a built-in provider and connect with an API key",
        providerIds: providerIdsByGroup("third-party")
      },
      {
        id: "custom",
        label: "Custom Provider",
        description: "Manually connect a local server, proxy, or unsupported provider",
        providerIds: providerIdsByGroup("custom")
      }
    ]
  };
}
__name(buildAuthProviderCatalog, "buildAuthProviderCatalog");
function parseStringArray2(value) {
  if (!Array.isArray(value)) return void 0;
  const result = value.map((item) => typeof item === "string" ? item.trim() : "").filter((item) => item.length > 0);
  return result.length > 0 ? [...new Set(result)] : void 0;
}
__name(parseStringArray2, "parseStringArray");
function parsePositiveBoundedInteger(value, max) {
  if (typeof value !== "number" || !Number.isInteger(value) || !Number.isFinite(value) || value <= 0 || value > max) {
    return void 0;
  }
  return value;
}
__name(parsePositiveBoundedInteger, "parsePositiveBoundedInteger");
function parseIPv4MappedHexSuffix(suffix) {
  const hexParts = suffix.split(":");
  if (hexParts.length !== 2) return void 0;
  const [hiRaw, loRaw] = hexParts;
  if (!/^[0-9a-f]{1,4}$/i.test(hiRaw) || !/^[0-9a-f]{1,4}$/i.test(loRaw)) {
    return void 0;
  }
  const hi = Number.parseInt(hiRaw, 16);
  const lo = Number.parseInt(loRaw, 16);
  return `${hi >> 8 & 255}.${hi & 255}.${lo >> 8 & 255}.${lo & 255}`;
}
__name(parseIPv4MappedHexSuffix, "parseIPv4MappedHexSuffix");
function parseIPv6FirstHextet(host) {
  const first = host.split(":", 1)[0];
  if (!first || !/^[0-9a-f]{1,4}$/i.test(first)) return void 0;
  return Number.parseInt(first, 16);
}
__name(parseIPv6FirstHextet, "parseIPv6FirstHextet");
function parseLegacyIPv4Part(value, max) {
  let parsed;
  if (/^0x[0-9a-f]+$/i.test(value)) {
    parsed = Number.parseInt(value.slice(2), 16);
  } else if (/^0[0-7]+$/.test(value)) {
    parsed = Number.parseInt(value, 8);
  } else if (/^[0-9]+$/.test(value)) {
    parsed = Number.parseInt(value, 10);
  } else {
    return void 0;
  }
  return parsed <= max ? parsed : void 0;
}
__name(parseLegacyIPv4Part, "parseLegacyIPv4Part");
function parseLegacyIPv4Host(host) {
  const parts = host.split(".");
  if (parts.length < 1 || parts.length > 4 || parts.some((part) => !part)) {
    return void 0;
  }
  const maxLastPart = [4294967295, 16777215, 65535, 255][parts.length - 1];
  if (maxLastPart === void 0) return void 0;
  const parsed = parts.map(
    (part, index) => parseLegacyIPv4Part(part, index === parts.length - 1 ? maxLastPart : 255)
  );
  if (parsed.some((part) => part === void 0)) return void 0;
  const values = parsed;
  const numeric = values.length === 1 ? values[0] : values.length === 2 ? values[0] * 16777216 + values[1] : values.length === 3 ? values[0] * 16777216 + values[1] * 65536 + values[2] : values[0] * 16777216 + values[1] * 65536 + values[2] * 256 + values[3];
  return [
    Math.floor(numeric / 16777216) & 255,
    Math.floor(numeric / 65536) & 255,
    Math.floor(numeric / 256) & 255,
    numeric & 255
  ].join(".");
}
__name(parseLegacyIPv4Host, "parseLegacyIPv4Host");
function isBlockedAuthProviderHost(hostname) {
  const stripped = hostname.endsWith(".") ? hostname.slice(0, -1) : hostname;
  const host = stripped.toLowerCase();
  if (host === "localhost" || host.endsWith(".localhost")) return true;
  const bareHost = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
  const blocklistHost = parseLegacyIPv4Host(bareHost) ?? bareHost;
  const ipVersion = net.isIP(blocklistHost);
  if (ipVersion === 4) {
    const parts = blocklistHost.split(".").map((part) => Number(part));
    const [a, b] = parts;
    return a === 0 || a === 10 || a === 127 || a === 100 && b !== void 0 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b !== void 0 && b >= 16 && b <= 31 || a === 192 && b === 168;
  }
  if (ipVersion === 6) {
    if (blocklistHost === "::" || blocklistHost === "::1") return true;
    const firstHextet = parseIPv6FirstHextet(blocklistHost);
    if (firstHextet !== void 0 && (firstHextet >= 65152 && firstHextet <= 65215 || (firstHextet & 65024) === 64512)) {
      return true;
    }
    if (blocklistHost.startsWith("::ffff:")) {
      const suffix = blocklistHost.slice("::ffff:".length);
      if (net.isIP(suffix) === 4) {
        return isBlockedAuthProviderHost(suffix);
      }
      const mappedIPv4 = parseIPv4MappedHexSuffix(suffix);
      return mappedIPv4 ? isBlockedAuthProviderHost(mappedIPv4) : true;
    }
  }
  return false;
}
__name(isBlockedAuthProviderHost, "isBlockedAuthProviderHost");
function parseAuthProviderBaseUrl(value, allowPrivateBaseUrl) {
  if (value === void 0 || value === null) return void 0;
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  if (!trimmed) return void 0;
  let parsed;
  try {
    parsed = new URL(trimmed);
  } catch {
    return null;
  }
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
  if (parsed.username || parsed.password) return null;
  if (!allowPrivateBaseUrl && isBlockedAuthProviderHost(parsed.hostname)) {
    return null;
  }
  return parsed.toString().replace(/\/$/, "");
}
__name(parseAuthProviderBaseUrl, "parseAuthProviderBaseUrl");
function parseAuthProviderInstallRequest(body, options) {
  const providerId = body["providerId"];
  const apiKey = body["apiKey"];
  if (typeof providerId !== "string" || providerId.trim().length === 0 || typeof apiKey !== "string" || apiKey.trim().length === 0) {
    return {
      ok: false,
      code: "invalid_request",
      error: "`providerId` and `apiKey` are required"
    };
  }
  const protocol = body["protocol"];
  const baseUrl = parseAuthProviderBaseUrl(
    body["baseUrl"],
    options?.allowPrivateBaseUrl === true
  );
  if (baseUrl === null) {
    return {
      ok: false,
      code: "invalid_base_url",
      error: "`baseUrl` must be an http(s) URL without credentials or blocked private-network host"
    };
  }
  const modelIds = parseStringArray2(body["modelIds"]);
  const rawAdvanced = body["advancedConfig"] && typeof body["advancedConfig"] === "object" ? body["advancedConfig"] : void 0;
  const rawMultimodal = rawAdvanced?.["multimodal"] && typeof rawAdvanced["multimodal"] === "object" ? rawAdvanced["multimodal"] : void 0;
  const contextWindowSize = parsePositiveBoundedInteger(
    rawAdvanced?.["contextWindowSize"],
    1e7
  );
  const maxTokens = parsePositiveBoundedInteger(
    rawAdvanced?.["maxTokens"],
    1e7
  );
  const advancedConfig = rawAdvanced ? {
    ...typeof rawAdvanced["enableThinking"] === "boolean" ? { enableThinking: rawAdvanced["enableThinking"] } : {},
    ...rawMultimodal ? {
      multimodal: {
        ...typeof rawMultimodal["image"] === "boolean" ? { image: rawMultimodal["image"] } : {},
        ...typeof rawMultimodal["pdf"] === "boolean" ? { pdf: rawMultimodal["pdf"] } : {},
        ...typeof rawMultimodal["audio"] === "boolean" ? { audio: rawMultimodal["audio"] } : {},
        ...typeof rawMultimodal["video"] === "boolean" ? { video: rawMultimodal["video"] } : {}
      }
    } : {},
    ...contextWindowSize !== void 0 ? { contextWindowSize } : {},
    ...maxTokens !== void 0 ? { maxTokens } : {}
  } : void 0;
  return {
    ok: true,
    value: {
      providerId: providerId.trim(),
      ...typeof protocol === "string" && protocol.trim() ? {
        protocol: protocol.trim()
      } : {},
      ...baseUrl ? { baseUrl } : {},
      apiKey,
      ...modelIds ? { modelIds } : {},
      ...advancedConfig ? { advancedConfig } : {}
    }
  };
}
__name(parseAuthProviderInstallRequest, "parseAuthProviderInstallRequest");

// packages/cli/src/serve/routes/workspace-auth.ts
function callerIsDeviceFlowInitiator(view, callerClientId) {
  return view.initiatorClientId === void 0 && callerClientId === void 0 || view.initiatorClientId !== void 0 && callerClientId !== void 0 && callerClientId === view.initiatorClientId;
}
__name(callerIsDeviceFlowInitiator, "callerIsDeviceFlowInitiator");
function toDeviceFlowStartResponseBody(view, attached, callerClientId) {
  const body = {
    deviceFlowId: view.deviceFlowId,
    providerId: view.providerId,
    status: view.status,
    expiresAt: view.expiresAt ?? 0,
    intervalMs: view.intervalMs ?? 0,
    attached
  };
  if (callerIsDeviceFlowInitiator(view, callerClientId)) {
    body["userCode"] = view.userCode ?? "";
    body["verificationUri"] = view.verificationUri ?? "";
    if (view.verificationUriComplete) {
      body["verificationUriComplete"] = view.verificationUriComplete;
    }
  }
  if (view.initiatorClientId && callerClientId !== void 0 && callerClientId === view.initiatorClientId) {
    body["initiatorClientId"] = view.initiatorClientId;
  }
  return body;
}
__name(toDeviceFlowStartResponseBody, "toDeviceFlowStartResponseBody");
function toDeviceFlowStateBody(view, callerClientId) {
  const body = {
    deviceFlowId: view.deviceFlowId,
    providerId: view.providerId,
    status: view.status,
    createdAt: view.createdAt
  };
  if (view.errorKind) body["errorKind"] = view.errorKind;
  if (view.hint) body["hint"] = view.hint;
  if (view.expiresAt !== void 0) body["expiresAt"] = view.expiresAt;
  if (view.intervalMs !== void 0) body["intervalMs"] = view.intervalMs;
  if (view.lastPolledAt !== void 0) body["lastPolledAt"] = view.lastPolledAt;
  if (callerIsDeviceFlowInitiator(view, callerClientId)) {
    if (view.userCode) body["userCode"] = view.userCode;
    if (view.verificationUri) body["verificationUri"] = view.verificationUri;
    if (view.verificationUriComplete) {
      body["verificationUriComplete"] = view.verificationUriComplete;
    }
    if (view.initiatorClientId) {
      body["initiatorClientId"] = view.initiatorClientId;
    }
  }
  return body;
}
__name(toDeviceFlowStateBody, "toDeviceFlowStateBody");
function registerWorkspaceAuthRoutes(app, deps) {
  const {
    mutate,
    deviceFlowRegistry,
    getSupportedDeviceFlowProviders,
    sendBridgeError: sendBridgeError2,
    boundWorkspace,
    allowPrivateAuthBaseUrl,
    installAuthProvider
  } = deps;
  app.post(
    "/workspace/auth/device-flow",
    mutate({ strict: true }),
    async (req, res) => {
      const body = safeBody(req);
      const providerIdRaw = body["providerId"];
      if (typeof providerIdRaw !== "string" || providerIdRaw.length === 0) {
        res.status(400).json({
          error: "`providerId` must be a non-empty string",
          code: "invalid_request"
        });
        return;
      }
      const supportedProviders = getSupportedDeviceFlowProviders();
      if (!supportedProviders.includes(providerIdRaw)) {
        res.status(400).json({
          error: `Unsupported device-flow provider: ${providerIdRaw}`,
          code: "unsupported_provider",
          supportedProviders
        });
        return;
      }
      const providerId = providerIdRaw;
      const clientId = parseClientIdHeader(req, res);
      if (clientId === null) return;
      try {
        const { view, attached } = await deviceFlowRegistry.start({
          providerId,
          ...clientId !== void 0 ? { initiatorClientId: clientId } : {}
        });
        res.status(attached ? 200 : 201).json(toDeviceFlowStartResponseBody(view, attached, clientId));
      } catch (err) {
        if (err instanceof UnsupportedDeviceFlowProviderError) {
          res.status(400).json({ error: err.message, code: "unsupported_provider" });
          return;
        }
        if (err instanceof TooManyActiveDeviceFlowsError) {
          res.status(409).json({ error: err.message, code: "too_many_active_flows" });
          return;
        }
        if (err instanceof UpstreamDeviceFlowError) {
          res.status(502).json({ error: err.message, code: "upstream_error" });
          return;
        }
        sendBridgeError2(res, err, {
          route: "POST /workspace/auth/device-flow"
        });
      }
    }
  );
  app.get(
    "/workspace/auth/device-flow/:id",
    mutate({ strict: true }),
    async (req, res) => {
      const id = req.params["id"];
      if (!id) {
        res.status(404).json({
          error: "Device-flow id required",
          code: "device_flow_not_found"
        });
        return;
      }
      const view = deviceFlowRegistry.get(id);
      if (!view) {
        res.status(404).json({
          error: `Device-flow ${id} not found`,
          code: "device_flow_not_found"
        });
        return;
      }
      const clientId = parseClientIdHeader(req, res);
      if (clientId === null) return;
      if (!callerIsDeviceFlowInitiator(view, clientId) && isServeDebugMode()) {
        writeStderrLine(
          `qwen serve debug: GET /workspace/auth/device-flow/${id} redacted verification fields \u2014 caller-clientId mismatch (initiator=${view.initiatorClientId ?? "anonymous"}, caller=${clientId ?? "anonymous"})`
        );
      }
      res.status(200).json(toDeviceFlowStateBody(view, clientId));
    }
  );
  app.delete(
    "/workspace/auth/device-flow/:id",
    mutate({ strict: true }),
    (req, res) => {
      const id = req.params["id"];
      if (!id) {
        res.status(404).json({
          error: "Device-flow id required",
          code: "device_flow_not_found"
        });
        return;
      }
      const clientId = parseClientIdHeader(req, res);
      if (clientId === null) return;
      const result = deviceFlowRegistry.cancel(id, clientId);
      if (result === void 0) {
        res.status(404).json({
          error: `Device-flow ${id} not found`,
          code: "device_flow_not_found"
        });
        return;
      }
      res.status(204).end();
    }
  );
  app.get("/workspace/auth/status", (_req, res) => {
    const pending = deviceFlowRegistry.listPending();
    res.status(200).json({
      v: 1,
      workspaceCwd: boundWorkspace,
      providers: [],
      pendingDeviceFlows: pending.map((view) => ({
        deviceFlowId: view.deviceFlowId,
        providerId: view.providerId,
        ...view.expiresAt !== void 0 ? { expiresAt: view.expiresAt } : {}
      })),
      // Derive from runtime provider map (single source of truth).
      supportedDeviceFlowProviders: getSupportedDeviceFlowProviders()
    });
  });
  app.get("/workspace/auth/providers", (_req, res) => {
    res.status(200).json(buildAuthProviderCatalog(boundWorkspace));
  });
  app.post(
    "/workspace/auth/provider",
    mutate({ strict: true }),
    async (req, res) => {
      if (!installAuthProvider) {
        res.status(501).json({
          error: "Auth provider installation is not implemented by this daemon",
          code: "not_implemented"
        });
        return;
      }
      const parsed = parseAuthProviderInstallRequest(safeBody(req), {
        allowPrivateBaseUrl: allowPrivateAuthBaseUrl
      });
      if (!parsed.ok) {
        res.status(400).json({
          error: parsed.error,
          code: parsed.code
        });
        return;
      }
      const installRequest = parsed.value;
      const knownProvider = ALL_PROVIDERS.find(
        (provider) => provider.id === installRequest.providerId
      );
      if (!knownProvider) {
        res.status(400).json({
          error: `Unsupported auth provider: ${installRequest.providerId}`,
          code: "unsupported_provider"
        });
        return;
      }
      if (installRequest.protocol) {
        const allowedProtocols = knownProvider.protocolOptions && knownProvider.protocolOptions.length ? knownProvider.protocolOptions : [knownProvider.protocol];
        if (!allowedProtocols.includes(installRequest.protocol)) {
          res.status(400).json({
            error: `protocol must be one of: ${allowedProtocols.join(", ")}`,
            code: "unsupported_protocol"
          });
          return;
        }
      }
      try {
        res.status(200).json(await installAuthProvider(installRequest));
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/auth/provider",
          providerId: installRequest.providerId
        });
      }
    }
  );
}
__name(registerWorkspaceAuthRoutes, "registerWorkspaceAuthRoutes");

// packages/cli/src/serve/routes/workspace-extensions.ts
init_esbuild_shims();
import * as crypto from "node:crypto";
function registerWorkspaceExtensionRoutes(app, deps) {
  const {
    boundWorkspace,
    bridge,
    workspace,
    mutate,
    safeBody: safeBody2,
    sendBridgeError: sendBridgeError2
  } = deps;
  const maxExtensionOperationHistory = deps.maxExtensionOperationHistory ?? 100;
  const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
  let extensionInstallQueue = Promise.resolve();
  let extensionInstallQueueDepth = 0;
  const MAX_EXTENSION_INSTALL_QUEUE_DEPTH = 10;
  const enqueueExtensionInstall = /* @__PURE__ */ __name(async (run) => {
    if (extensionInstallQueueDepth >= MAX_EXTENSION_INSTALL_QUEUE_DEPTH) {
      throw new Error("Extension operation queue is full");
    }
    extensionInstallQueueDepth += 1;
    const next = extensionInstallQueue.then(run, run).finally(() => {
      extensionInstallQueueDepth -= 1;
    });
    extensionInstallQueue = next.catch(() => void 0);
    return next;
  }, "enqueueExtensionInstall");
  const EXTENSION_MUTATION_TIMEOUT_MS = 10 * 6e4;
  const EXTENSION_REFRESH_TIMEOUT_MS = 3e4;
  const isExtensionQueueFullError = /* @__PURE__ */ __name((err) => err instanceof Error && err.message === "Extension operation queue is full", "isExtensionQueueFullError");
  const sendExtensionQueueFull = /* @__PURE__ */ __name((res) => {
    res.status(429).json({
      error: "Extension operation queue is full",
      code: "extension_queue_full"
    });
  }, "sendExtensionQueueFull");
  const withExtensionTimeout = /* @__PURE__ */ __name(async (promise, timeoutMs, operation) => await new Promise((resolve5, reject) => {
    const timeout = setTimeout(() => {
      reject(new Error(`${operation} timed out after ${timeoutMs}ms`));
    }, timeoutMs);
    promise.then(
      (value) => {
        clearTimeout(timeout);
        resolve5(value);
      },
      (err) => {
        clearTimeout(timeout);
        reject(err);
      }
    );
  }), "withExtensionTimeout");
  const createExtensionManager = /* @__PURE__ */ __name(() => new ExtensionManager({
    workspaceDir: boundWorkspace,
    isWorkspaceTrusted: getWorkspaceTrustStatus(
      loadSettings(boundWorkspace).merged,
      boundWorkspace
    ).effective.state === "trusted",
    requestConsent: /* @__PURE__ */ __name(() => Promise.resolve(), "requestConsent"),
    requestSetting: /* @__PURE__ */ __name(async (setting) => {
      throw new Error(
        `Extension setting "${setting.envVar}" requires interactive configuration and is not supported over the daemon install endpoint.`
      );
    }, "requestSetting"),
    requestChoicePlugin: /* @__PURE__ */ __name(async () => {
      throw new Error(
        "Marketplace plugin selection is not supported over the daemon install endpoint. Specify a plugin name in the source."
      );
    }, "requestChoicePlugin")
  }), "createExtensionManager");
  const validateExtensionMutationClient = /* @__PURE__ */ __name((req, res, route) => {
    const clientId = parseAndValidateWorkspaceClientId(req, res, bridge);
    if (clientId === null) return false;
    if (clientId === void 0) {
      res.status(400).json({
        error: "Missing X-Qwen-Client-Id header",
        code: "missing_client_id"
      });
      return false;
    }
    buildWorkspaceCtx(route, clientId);
    return true;
  }, "validateExtensionMutationClient");
  const parseExtensionScope = /* @__PURE__ */ __name((body, res) => {
    const scope = body["scope"];
    if (scope !== "user" && scope !== "workspace") {
      res.status(400).json({ error: '`scope` must be either "user" or "workspace"' });
      return null;
    }
    return scope === "user" ? "User" /* User */ : "Workspace" /* Workspace */;
  }, "parseExtensionScope");
  const parseExtensionRegistryUrl = /* @__PURE__ */ __name((value, res) => {
    let parsed;
    try {
      parsed = new URL(value);
    } catch {
      res.status(400).json({ error: "`registry` must be a valid URL" });
      return null;
    }
    if (parsed.protocol !== "https:") {
      res.status(400).json({ error: "`registry` must use https" });
      return null;
    }
    if (parsed.username || parsed.password) {
      res.status(400).json({ error: "`registry` must not include credentials" });
      return null;
    }
    if (isBlockedAuthProviderHost(parsed.hostname)) {
      res.status(400).json({ error: "`registry` host is not allowed" });
      return null;
    }
    return parsed.toString().replace(/\/$/, "");
  }, "parseExtensionRegistryUrl");
  const parsePotentialSourceUrl = /* @__PURE__ */ __name((source) => {
    if (/^[a-zA-Z]:[\\/]/.test(source)) return null;
    try {
      return new URL(source);
    } catch {
      const sshMatch = /^(?:[^@]+@)?(\[[^\]]+\]|[^:]+):/.exec(source);
      if (!sshMatch?.[1]) return null;
      try {
        return new URL(`ssh://${sshMatch[1]}`);
      } catch {
        return null;
      }
    }
  }, "parsePotentialSourceUrl");
  const validateExtensionSourceHost = /* @__PURE__ */ __name((source, res) => {
    const parsed = parsePotentialSourceUrl(source);
    if (!parsed) return true;
    if (parsed.username || parsed.password) {
      res.status(400).json({ error: "`source` must not include credentials" });
      return false;
    }
    if (isBlockedAuthProviderHost(parsed.hostname)) {
      res.status(400).json({ error: "`source` host is not allowed" });
      return false;
    }
    if (parsed.protocol !== "https:" && parsed.protocol !== "ssh:") {
      res.status(400).json({ error: "`source` must use https or ssh" });
      return false;
    }
    return true;
  }, "validateExtensionSourceHost");
  const validateExtensionSourceMetadata = /* @__PURE__ */ __name((installMetadata) => {
    if (installMetadata.type !== "git") return true;
    const parsed = parsePotentialSourceUrl(installMetadata.source);
    return !!parsed && (parsed.protocol === "https:" || parsed.protocol === "ssh:") && !isBlockedAuthProviderHost(parsed.hostname);
  }, "validateExtensionSourceMetadata");
  const findLoadedExtension = /* @__PURE__ */ __name((extensionManager, extensionName) => {
    const requested = extensionName.toLowerCase();
    const extensions = extensionManager.getLoadedExtensions();
    const byName = extensions.find(
      (extension) => extension.name.toLowerCase() === requested
    );
    if (byName) return byName;
    if (!extensionName.includes("://") && !extensionName.includes("@")) {
      return void 0;
    }
    return extensions.find(
      (extension) => extension.installMetadata?.source?.toLowerCase() === requested
    );
  }, "findLoadedExtension");
  const extensionOperations = /* @__PURE__ */ new Map();
  const isTerminalExtensionOperation = /* @__PURE__ */ __name((operation) => operation.status !== "queued" && operation.status !== "running", "isTerminalExtensionOperation");
  const redactExtensionOperationResult = /* @__PURE__ */ __name((event) => ({
    ...event,
    ...event.source ? { source: redactUrlCredentials(event.source) } : {}
  }), "redactExtensionOperationResult");
  const rememberExtensionOperation = /* @__PURE__ */ __name((operation) => {
    extensionOperations.set(operation.operationId, operation);
    while (extensionOperations.size > maxExtensionOperationHistory) {
      let evicted = false;
      for (const [id, storedOperation] of extensionOperations) {
        if (!isTerminalExtensionOperation(storedOperation)) continue;
        extensionOperations.delete(id);
        evicted = true;
        break;
      }
      if (!evicted) break;
    }
  }, "rememberExtensionOperation");
  const updateExtensionOperation = /* @__PURE__ */ __name((operationId, patch) => {
    const current = extensionOperations.get(operationId);
    if (!current) return;
    extensionOperations.set(operationId, {
      ...current,
      ...patch,
      updatedAt: Date.now()
    });
  }, "updateExtensionOperation");
  const runQueuedExtensionMutation = /* @__PURE__ */ __name((operation, failureContext, res, run) => {
    if (extensionInstallQueueDepth >= MAX_EXTENSION_INSTALL_QUEUE_DEPTH) {
      sendExtensionQueueFull(res);
      return;
    }
    const operationId = crypto.randomUUID();
    const now = Date.now();
    rememberExtensionOperation({
      v: 1,
      operationId,
      operation,
      status: "queued",
      createdAt: now,
      updatedAt: now,
      ...failureContext.source ? { source: redactUrlCredentials(failureContext.source) } : {},
      ...failureContext.name ? { name: failureContext.name } : {}
    });
    res.status(202).json({ accepted: true, operationId });
    void enqueueExtensionInstall(async () => {
      try {
        updateExtensionOperation(operationId, { status: "running" });
        const extensionManager = createExtensionManager();
        await extensionManager.refreshCache();
        const event = await withExtensionTimeout(
          run(extensionManager),
          EXTENSION_MUTATION_TIMEOUT_MS,
          `extension ${operation}`
        );
        extensionsStatusCache = void 0;
        try {
          const result = await bridge.refreshExtensionsForAllSessions(event);
          updateExtensionOperation(operationId, {
            status: "succeeded",
            result: {
              ...redactExtensionOperationResult(event),
              refreshed: result.refreshed,
              failed: result.failed
            }
          });
          writeStderrLine(
            `qwen serve: extensions ${operation}: refreshed ${result.refreshed} session(s), ${result.failed} failed`
          );
        } catch (refreshErr) {
          const message = redactUrlCredentials(
            refreshErr instanceof Error ? refreshErr.message : String(refreshErr)
          );
          updateExtensionOperation(operationId, {
            status: "succeeded_with_refresh_error",
            result: {
              ...redactExtensionOperationResult(event),
              refreshed: 0,
              failed: 1,
              error: message.slice(0, 500)
            }
          });
          try {
            bridge.broadcastExtensionsChanged({
              ...event,
              refreshed: 0,
              failed: 1,
              error: message.slice(0, 500)
            });
          } catch (broadcastErr) {
            writeStderrLine(
              `qwen serve: extensions ${operation}: failed to broadcast refresh failure: ${broadcastErr instanceof Error ? redactUrlCredentials(broadcastErr.message) : String(broadcastErr)}`
            );
          }
          writeStderrLine(
            `qwen serve: extensions ${operation}: mutation succeeded but refresh failed: ${message}`
          );
        }
      } catch (err) {
        const message = redactUrlCredentials(
          err instanceof Error ? err.message : String(err)
        );
        updateExtensionOperation(operationId, {
          status: "failed",
          error: message.slice(0, 500)
        });
        try {
          bridge.broadcastExtensionsChanged({
            status: "failed",
            ...failureContext.source ? { source: redactUrlCredentials(failureContext.source) } : {},
            ...failureContext.name ? { name: failureContext.name } : {},
            refreshed: 0,
            failed: 0,
            error: message.slice(0, 500)
          });
        } catch (broadcastErr) {
          writeStderrLine(
            `qwen serve: extensions ${operation}: failed to broadcast failure: ${broadcastErr instanceof Error ? redactUrlCredentials(broadcastErr.message) : String(broadcastErr)}`
          );
        }
        try {
          writeStderrLine(
            `qwen serve: extensions ${operation}: background task failed: ${message}`
          );
        } catch {
        }
      }
    }).catch((err) => {
      const message = redactUrlCredentials(
        err instanceof Error ? err.message : String(err)
      );
      updateExtensionOperation(operationId, {
        status: "failed",
        error: message.slice(0, 500)
      });
      try {
        writeStderrLine(
          `qwen serve: extensions ${operation}: queued task failed: ${message}`
        );
      } catch {
      }
    });
  }, "runQueuedExtensionMutation");
  let extensionsStatusCache;
  const buildLocalExtensionsStatus = /* @__PURE__ */ __name(async () => {
    const now = Date.now();
    if (extensionsStatusCache && extensionsStatusCache.expiresAt > now) {
      return extensionsStatusCache.value;
    }
    const extensionManager = createExtensionManager();
    await extensionManager.refreshCache();
    const entries = extensionManager.getLoadedExtensions().map((ext) => {
      const capabilities = {
        mcpServerCount: ext.mcpServers ? Object.keys(ext.mcpServers).length : 0,
        skillCount: ext.skills?.length ?? 0,
        agentCount: ext.agents?.length ?? 0,
        hookCount: ext.hooks ? Object.values(ext.hooks).reduce(
          (sum, defs) => sum + (defs?.length ?? 0),
          0
        ) : 0,
        commandCount: ext.commands?.length ?? 0,
        contextFileCount: ext.contextFiles.length,
        channelCount: ext.channels ? Object.keys(ext.channels).length : 0,
        hasSettings: (ext.settings?.length ?? 0) > 0
      };
      return {
        kind: "extension",
        id: ext.id,
        name: ext.name,
        ...ext.displayName ? { displayName: ext.displayName } : {},
        ...ext.config.description ? { description: ext.config.description } : {},
        version: ext.version,
        isActive: ext.isActive,
        path: ext.path,
        ...ext.installMetadata?.source ? { source: redactUrlCredentials(ext.installMetadata.source) } : {},
        ...ext.installMetadata?.type ? { installType: ext.installMetadata.type } : {},
        ...ext.installMetadata?.originSource ? { originSource: ext.installMetadata.originSource } : {},
        ...ext.installMetadata?.ref ? { ref: ext.installMetadata.ref } : {},
        ...ext.installMetadata?.autoUpdate !== void 0 ? { autoUpdate: ext.installMetadata.autoUpdate } : {},
        updateState: ext.installMetadata ? "unknown" : "not updatable",
        capabilities,
        details: {
          mcpServers: ext.mcpServers ? Object.keys(ext.mcpServers) : [],
          commands: ext.commands ?? [],
          skills: ext.skills?.map((skill) => skill.name) ?? [],
          agents: ext.agents?.map((agent) => agent.name) ?? [],
          contextFiles: ext.contextFiles,
          settings: ext.resolvedSettings?.map((setting) => setting.name) ?? []
        }
      };
    });
    const status = {
      v: STATUS_SCHEMA_VERSION,
      workspaceCwd: boundWorkspace,
      initialized: true,
      extensions: entries
    };
    extensionsStatusCache = {
      expiresAt: now + 2e3,
      value: status
    };
    return status;
  }, "buildLocalExtensionsStatus");
  app.get("/workspace/extensions", async (_req, res) => {
    try {
      buildWorkspaceCtx("GET /workspace/extensions");
      res.status(200).json(await buildLocalExtensionsStatus());
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/extensions" });
    }
  });
  app.get("/workspace/extensions/operations/:operationId", async (req, res) => {
    try {
      buildWorkspaceCtx("GET /workspace/extensions/operations/:operationId");
      const operationId = req.params["operationId"];
      if (!operationId) {
        res.status(400).json({ error: "Missing extension operation id" });
        return;
      }
      const operation = extensionOperations.get(operationId);
      if (!operation) {
        res.status(404).json({
          error: `Extension operation "${operationId}" not found`,
          code: "extension_operation_not_found"
        });
        return;
      }
      res.status(200).json(operation);
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /workspace/extensions/operations/:operationId"
      });
    }
  });
  app.post(
    "/workspace/extensions/install",
    mutate({ strict: true }),
    async (req, res) => {
      try {
        if (!validateExtensionMutationClient(
          req,
          res,
          "POST /workspace/extensions/install"
        )) {
          return;
        }
        const body = safeBody2(req);
        const source = body["source"];
        const ref = body["ref"];
        const autoUpdate = body["autoUpdate"];
        const allowPreRelease = body["allowPreRelease"];
        const registry = body["registry"];
        const consent = body["consent"];
        if (!source || typeof source !== "string") {
          res.status(400).json({ error: "Missing or invalid source" });
          return;
        }
        if (ref !== void 0 && (typeof ref !== "string" || ref === "")) {
          res.status(400).json({ error: "`ref` must be a string" });
          return;
        }
        if (typeof ref === "string" && ref.startsWith("-")) {
          res.status(400).json({ error: '`ref` must not start with "-"' });
          return;
        }
        if (autoUpdate !== void 0 && typeof autoUpdate !== "boolean") {
          res.status(400).json({ error: "`autoUpdate` must be a boolean" });
          return;
        }
        if (allowPreRelease !== void 0 && typeof allowPreRelease !== "boolean") {
          res.status(400).json({ error: "`allowPreRelease` must be a boolean" });
          return;
        }
        if (registry !== void 0 && typeof registry !== "string") {
          res.status(400).json({ error: "`registry` must be a string" });
          return;
        }
        const sourceValue = source;
        const refValue = typeof ref === "string" ? ref : void 0;
        const autoUpdateValue = typeof autoUpdate === "boolean" ? autoUpdate : void 0;
        const allowPreReleaseValue = typeof allowPreRelease === "boolean" ? allowPreRelease : void 0;
        const registryValue = typeof registry === "string" ? registry : void 0;
        const registryUrl = registryValue !== void 0 ? parseExtensionRegistryUrl(registryValue, res) : void 0;
        if (registryUrl === null) return;
        if (consent !== true) {
          res.status(400).json({
            error: "Extension installation requires explicit consent"
          });
          return;
        }
        if (!validateExtensionSourceHost(sourceValue, res)) {
          return;
        }
        runQueuedExtensionMutation(
          "install",
          { source: sourceValue },
          res,
          async (extensionManager) => {
            const installMetadata = await parseInstallSource(sourceValue);
            if (installMetadata.type !== "git" && installMetadata.type !== "github-release" && installMetadata.type !== "npm") {
              throw new Error(
                "Only GitHub, Git, and npm extension installs are supported over the daemon endpoint."
              );
            }
            if (installMetadata.type === "npm" && refValue) {
              throw new Error("--ref is not applicable for npm extensions.");
            }
            if (installMetadata.type !== "npm" && registryValue) {
              throw new Error(
                "--registry is only applicable for npm extensions."
              );
            }
            if (!validateExtensionSourceMetadata(installMetadata)) {
              throw new Error("`source` host is not allowed");
            }
            if (installMetadata.type === "npm" && registryUrl) {
              installMetadata.registryUrl = registryUrl;
            }
            const extension = await extensionManager.installExtension(
              {
                ...installMetadata,
                ref: refValue,
                autoUpdate: autoUpdateValue,
                allowPreRelease: allowPreReleaseValue
              },
              () => Promise.resolve()
            );
            return {
              status: "installed",
              source: sourceValue,
              name: extension.name,
              version: extension.config.version
            };
          }
        );
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/extensions/install"
        });
      }
    }
  );
  app.post(
    "/workspace/extensions/check-updates",
    mutate({ strict: true }),
    async (req, res) => {
      try {
        if (!validateExtensionMutationClient(
          req,
          res,
          "POST /workspace/extensions/check-updates"
        )) {
          return;
        }
        const states = await enqueueExtensionInstall(
          async () => withExtensionTimeout(
            (async () => {
              const extensionManager = createExtensionManager();
              await extensionManager.refreshCache();
              const updateStates = {};
              await extensionManager.checkForAllExtensionUpdates(
                (name, state) => {
                  updateStates[name] = state;
                }
              );
              return updateStates;
            })(),
            EXTENSION_REFRESH_TIMEOUT_MS,
            "extension update check"
          )
        );
        res.status(200).json({ states });
      } catch (err) {
        if (isExtensionQueueFullError(err)) {
          sendExtensionQueueFull(res);
          return;
        }
        sendBridgeError2(res, err, {
          route: "POST /workspace/extensions/check-updates"
        });
      }
    }
  );
  app.post(
    "/workspace/extensions/refresh",
    mutate({ strict: true }),
    async (req, res) => {
      try {
        if (!validateExtensionMutationClient(
          req,
          res,
          "POST /workspace/extensions/refresh"
        )) {
          return;
        }
        const result = await enqueueExtensionInstall(
          async () => withExtensionTimeout(
            workspace.refreshExtensionsForAllSessions(),
            EXTENSION_REFRESH_TIMEOUT_MS,
            "extension refresh"
          )
        );
        res.status(200).json(result);
      } catch (err) {
        if (isExtensionQueueFullError(err)) {
          sendExtensionQueueFull(res);
          return;
        }
        sendBridgeError2(res, err, {
          route: "POST /workspace/extensions/refresh"
        });
      }
    }
  );
  app.post(
    "/workspace/extensions/:name/enable",
    mutate({ strict: true }),
    async (req, res) => {
      try {
        if (!validateExtensionMutationClient(
          req,
          res,
          "POST /workspace/extensions/:name/enable"
        )) {
          return;
        }
        const name = req.params["name"];
        if (!name) {
          res.status(400).json({ error: "Missing extension name" });
          return;
        }
        const scope = parseExtensionScope(safeBody2(req), res);
        if (scope === null) return;
        runQueuedExtensionMutation(
          "enable",
          { name },
          res,
          async (extensionManager) => {
            const extension = findLoadedExtension(extensionManager, name);
            if (!extension) {
              throw new Error(`Extension "${name}" not found`);
            }
            await extensionManager.enableExtension(
              extension.name,
              scope,
              boundWorkspace
            );
            return { status: "enabled", name: extension.name };
          }
        );
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/extensions/:name/enable"
        });
      }
    }
  );
  app.post(
    "/workspace/extensions/:name/disable",
    mutate({ strict: true }),
    async (req, res) => {
      try {
        if (!validateExtensionMutationClient(
          req,
          res,
          "POST /workspace/extensions/:name/disable"
        )) {
          return;
        }
        const name = req.params["name"];
        if (!name) {
          res.status(400).json({ error: "Missing extension name" });
          return;
        }
        const scope = parseExtensionScope(safeBody2(req), res);
        if (scope === null) return;
        runQueuedExtensionMutation(
          "disable",
          { name },
          res,
          async (extensionManager) => {
            const extension = findLoadedExtension(extensionManager, name);
            if (!extension) {
              throw new Error(`Extension "${name}" not found`);
            }
            await extensionManager.disableExtension(
              extension.name,
              scope,
              boundWorkspace
            );
            return { status: "disabled", name: extension.name };
          }
        );
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/extensions/:name/disable"
        });
      }
    }
  );
  app.post(
    "/workspace/extensions/:name/update",
    mutate({ strict: true }),
    async (req, res) => {
      try {
        if (!validateExtensionMutationClient(
          req,
          res,
          "POST /workspace/extensions/:name/update"
        )) {
          return;
        }
        const name = req.params["name"];
        if (!name) {
          res.status(400).json({ error: "Missing extension name" });
          return;
        }
        runQueuedExtensionMutation(
          "update",
          { name },
          res,
          async (extensionManager) => {
            const extension = findLoadedExtension(extensionManager, name);
            if (!extension) {
              throw new Error(`Extension "${name}" not found`);
            }
            let updateError;
            const updateState = await withExtensionTimeout(
              checkForExtensionUpdate(extension, extensionManager).catch(
                (err) => {
                  updateError = err;
                  return "error" /* ERROR */;
                }
              ),
              EXTENSION_REFRESH_TIMEOUT_MS,
              "extension update check"
            );
            if (updateState === "error" /* ERROR */) {
              const message = updateError === void 0 ? void 0 : redactUrlCredentials(
                updateError instanceof Error ? updateError.message : String(updateError)
              );
              throw new Error(
                `Update check failed for extension "${extension.name}"${message ? `: ${message}` : ""}`
              );
            }
            if (updateState !== "update available" /* UPDATE_AVAILABLE */) {
              throw new Error(`Extension "${extension.name}" has no update`);
            }
            const info = await extensionManager.updateExtension(
              extension,
              updateState,
              () => void 0
            );
            return {
              status: "updated",
              name: extension.name,
              ...info?.updatedVersion ? { version: info.updatedVersion } : {}
            };
          }
        );
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/extensions/:name/update"
        });
      }
    }
  );
  app.delete(
    "/workspace/extensions/:name",
    mutate({ strict: true }),
    async (req, res) => {
      try {
        if (!validateExtensionMutationClient(
          req,
          res,
          "DELETE /workspace/extensions/:name"
        )) {
          return;
        }
        const name = req.params["name"];
        if (!name) {
          res.status(400).json({ error: "Missing extension name" });
          return;
        }
        runQueuedExtensionMutation(
          "uninstall",
          { name },
          res,
          async (extensionManager) => {
            const extension = findLoadedExtension(extensionManager, name);
            if (!extension) {
              throw new Error(`Extension "${name}" not found`);
            }
            await extensionManager.uninstallExtension(
              extension.name,
              false,
              boundWorkspace
            );
            return { status: "uninstalled", name: extension.name };
          }
        );
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "DELETE /workspace/extensions/:name"
        });
      }
    }
  );
}
__name(registerWorkspaceExtensionRoutes, "registerWorkspaceExtensionRoutes");

// packages/cli/src/serve/routes/workspace-file-write.ts
init_esbuild_shims();
function getFsFactory3(req, res) {
  const factory = req.app.locals.fsFactory;
  if (!factory) {
    applyReadHeaders(res);
    res.status(500).json({
      errorKind: "internal_error",
      error: "workspace filesystem factory is not configured",
      status: 500
    });
    return null;
  }
  return factory;
}
__name(getFsFactory3, "getFsFactory");
function sendParseError(res, _route, error2) {
  applyReadHeaders(res);
  res.status(400).json({
    errorKind: "parse_error",
    error: error2,
    status: 400
  });
  return null;
}
__name(sendParseError, "sendParseError");
function requireBodyString(body, key, res, route) {
  const value = body[key];
  if (typeof value !== "string" || value.length === 0) {
    return sendParseError(res, route, `\`${key}\` must be a non-empty string`);
  }
  return value;
}
__name(requireBodyString, "requireBodyString");
function optionalBoolean(body, key, res, route) {
  const value = body[key];
  if (value === void 0) return void 0;
  if (typeof value !== "boolean") {
    return sendParseError(res, route, `\`${key}\` must be a boolean`);
  }
  return value;
}
__name(optionalBoolean, "optionalBoolean");
function optionalString(body, key, res, route) {
  const value = body[key];
  if (value === void 0) return void 0;
  if (typeof value !== "string" || value.length === 0) {
    return sendParseError(res, route, `\`${key}\` must be a non-empty string`);
  }
  return value;
}
__name(optionalString, "optionalString");
function optionalLineEnding(body, res, route) {
  const value = body["lineEnding"];
  if (value === void 0) return void 0;
  if (value !== "crlf" && value !== "lf") {
    return sendParseError(res, route, '`lineEnding` must be "lf" or "crlf"');
  }
  return value;
}
__name(optionalLineEnding, "optionalLineEnding");
function requiredHash(body, res, route) {
  const value = body["expectedHash"];
  if (!isContentHash(value)) {
    return sendParseError(
      res,
      route,
      "`expectedHash` must match sha256:<64 lowercase hex chars>"
    );
  }
  return value;
}
__name(requiredHash, "requiredHash");
function optionalHash(body, res, route) {
  const value = body["expectedHash"];
  if (value === void 0) return void 0;
  if (!isContentHash(value)) {
    return sendParseError(
      res,
      route,
      "`expectedHash` must match sha256:<64 lowercase hex chars>"
    );
  }
  return value;
}
__name(optionalHash, "optionalHash");
function resolveOriginatorClientId2(clientId, deps, res) {
  if (clientId === void 0) return void 0;
  if (!deps.bridge.knownClientIds().has(clientId)) {
    applyReadHeaders(res);
    res.status(400).json({
      error: `Client id "${clientId}" is not registered for this workspace`,
      code: "invalid_client_id",
      clientId
    });
    return null;
  }
  return clientId;
}
__name(resolveOriginatorClientId2, "resolveOriginatorClientId");
async function handlePostFileWrite(req, res, deps) {
  const ROUTE2 = "POST /file/write";
  const factory = getFsFactory3(req, res);
  if (!factory) return;
  const body = deps.safeBody(req);
  const queryPath = requireBodyString(body, "path", res, ROUTE2);
  if (queryPath === null) return;
  const content = body["content"];
  if (typeof content !== "string") {
    sendParseError(res, ROUTE2, "`content` must be a string");
    return;
  }
  const rawMode = body["mode"];
  if (rawMode !== "create" && rawMode !== "replace") {
    sendParseError(res, ROUTE2, '`mode` must be "create" or "replace"');
    return;
  }
  const mode = rawMode;
  const expectedHash = mode === "replace" ? requiredHash(body, res, ROUTE2) : optionalHash(body, res, ROUTE2);
  if (expectedHash === null) return;
  const bom = optionalBoolean(body, "bom", res, ROUTE2);
  if (bom === null) return;
  const encoding = optionalString(body, "encoding", res, ROUTE2);
  if (encoding === null) return;
  const lineEnding = optionalLineEnding(body, res, ROUTE2);
  if (lineEnding === null) return;
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const originatorClientId = resolveOriginatorClientId2(clientId, deps, res);
  if (originatorClientId === null) return;
  const fs3 = factory.forRequest({
    originatorClientId,
    route: ROUTE2
  });
  try {
    const resolved = await fs3.resolve(queryPath, "write");
    const out = await fs3.writeTextAtomic(resolved, content, {
      mode,
      ...expectedHash ? { expectedHash } : {},
      ...bom !== void 0 ? { bom } : {},
      ...encoding !== void 0 ? { encoding } : {},
      ...lineEnding !== void 0 ? { lineEnding } : {}
    });
    applyReadHeaders(res);
    res.status(out.created ? 201 : 200).json({
      kind: "file_write",
      path: workspaceRelative(req, resolved),
      mode,
      created: out.created,
      sizeBytes: out.sizeBytes,
      hash: out.hash,
      encoding: out.meta.encoding ?? "utf-8",
      bom: out.meta.bom === true,
      lineEnding: out.meta.lineEnding,
      matchedIgnore: out.meta.matchedIgnore ?? null
    });
  } catch (err) {
    sendFsError(res, err, ROUTE2);
  }
}
__name(handlePostFileWrite, "handlePostFileWrite");
async function handlePostFileEdit(req, res, deps) {
  const ROUTE2 = "POST /file/edit";
  const factory = getFsFactory3(req, res);
  if (!factory) return;
  const body = deps.safeBody(req);
  const queryPath = requireBodyString(body, "path", res, ROUTE2);
  if (queryPath === null) return;
  const oldText = body["oldText"];
  if (typeof oldText !== "string") {
    sendParseError(res, ROUTE2, "`oldText` must be a string");
    return;
  }
  const newText = body["newText"];
  if (typeof newText !== "string") {
    sendParseError(res, ROUTE2, "`newText` must be a string");
    return;
  }
  const expectedHash = requiredHash(body, res, ROUTE2);
  if (expectedHash === null) return;
  const clientId = deps.parseClientId(req, res);
  if (clientId === null) return;
  const originatorClientId = resolveOriginatorClientId2(clientId, deps, res);
  if (originatorClientId === null) return;
  const fs3 = factory.forRequest({
    originatorClientId,
    route: ROUTE2
  });
  try {
    const resolved = await fs3.resolve(queryPath, "edit");
    const out = await fs3.editAtomic(resolved, oldText, newText, {
      expectedHash
    });
    applyReadHeaders(res);
    res.status(200).json({
      kind: "file_edit",
      path: workspaceRelative(req, resolved),
      replacements: 1,
      sizeBytes: out.writtenBytes,
      hash: out.hash,
      encoding: out.meta?.encoding ?? "utf-8",
      bom: out.meta?.bom === true,
      lineEnding: out.meta?.lineEnding ?? "lf",
      matchedIgnore: out.meta?.matchedIgnore ?? null
    });
  } catch (err) {
    sendFsError(res, err, ROUTE2);
  }
}
__name(handlePostFileEdit, "handlePostFileEdit");
function registerWorkspaceFileWriteRoutes(app, deps) {
  app.post(
    "/file/write",
    deps.mutate({ strict: true }),
    (req, res) => handlePostFileWrite(req, res, deps)
  );
  app.post(
    "/file/edit",
    deps.mutate({ strict: true }),
    (req, res) => handlePostFileEdit(req, res, deps)
  );
}
__name(registerWorkspaceFileWriteRoutes, "registerWorkspaceFileWriteRoutes");

// packages/cli/src/serve/routes/workspace-trust.ts
init_esbuild_shims();
function sendTrustError(res, route, err) {
  writeStderrLine(
    `qwen serve: ${route} error: ${err instanceof Error ? err.message : String(err)}`
  );
  if (err instanceof FatalConfigError) {
    res.status(500).json({
      error: "Failed to load trusted folders",
      code: "trusted_folders_invalid"
    });
    return;
  }
  res.status(500).json({
    error: "Failed to process workspace trust request",
    code: "internal_error"
  });
}
__name(sendTrustError, "sendTrustError");
function registerWorkspaceTrustRoutes(app, deps) {
  const {
    boundWorkspace,
    workspace,
    mutate,
    safeBody: safeBody2,
    parseAndValidateClientId
  } = deps;
  app.get("/workspace/trust", async (_req, res) => {
    try {
      const status = await workspace.getWorkspaceTrustStatus({
        route: "GET /workspace/trust",
        workspaceCwd: boundWorkspace
      });
      res.status(200).json(status);
    } catch (err) {
      sendTrustError(res, "GET /workspace/trust", err);
    }
  });
  app.post(
    "/workspace/trust/request",
    mutate({ strict: true }),
    async (req, res) => {
      const body = safeBody2(req);
      const desiredState = body["desiredState"];
      if (desiredState !== "trusted" && desiredState !== "untrusted") {
        res.status(400).json({
          error: 'desiredState must be "trusted" or "untrusted"',
          code: "invalid_desired_state"
        });
        return;
      }
      const reason = body["reason"];
      if (reason !== void 0 && (typeof reason !== "string" || reason.length > MAX_TRUST_REASON_LENGTH)) {
        res.status(400).json({
          error: `reason must be a string up to ${MAX_TRUST_REASON_LENGTH} characters`,
          code: "invalid_reason"
        });
        return;
      }
      const clientId = parseAndValidateClientId(req, res);
      if (clientId === null) return;
      const ctx = {
        route: "POST /workspace/trust/request",
        workspaceCwd: boundWorkspace,
        ...clientId !== void 0 ? { originatorClientId: clientId } : {}
      };
      try {
        const status = await workspace.getWorkspaceTrustStatus(ctx);
        if (!status.folderTrustEnabled) {
          res.status(409).json({
            error: "Folder trust is disabled for this workspace",
            code: "folder_trust_disabled"
          });
          return;
        }
        const result = await workspace.requestWorkspaceTrustChange(ctx, {
          desiredState,
          ...reason !== void 0 ? { reason } : {}
        });
        res.status(202).json(result);
      } catch (err) {
        sendTrustError(res, "POST /workspace/trust/request", err);
      }
    }
  );
}
__name(registerWorkspaceTrustRoutes, "registerWorkspaceTrustRoutes");

// packages/cli/src/serve/routes/permission.ts
init_esbuild_shims();
function registerPermissionRoutes(app, deps) {
  const { bridge, mutate, sendPermissionVoteError: sendPermissionVoteError2 } = deps;
  app.post("/session/:id/permission/:requestId", mutate(), (req, res) => {
    const sessionId = req.params["id"];
    const requestId = req.params["requestId"];
    const response = parsePermissionVoteBody(req, res);
    if (response === void 0) return;
    const clientId = parseClientIdHeader(req, res);
    if (clientId === null) return;
    const fromLoopback = detectFromLoopback(req);
    const context = {
      ...clientId !== void 0 ? { clientId } : {},
      fromLoopback
    };
    let accepted;
    try {
      accepted = bridge.respondToSessionPermission(
        sessionId,
        requestId,
        response,
        context
      );
    } catch (err) {
      sendPermissionVoteError2(res, err, {
        route: "POST /session/:id/permission/:requestId",
        sessionId
      });
      return;
    }
    if (!accepted) {
      res.status(404).json({
        error: "No pending permission request for session",
        sessionId,
        requestId
      });
      return;
    }
    res.status(200).json({});
  });
  app.post("/permission/:requestId", mutate(), (req, res) => {
    const requestId = req.params["requestId"];
    const response = parsePermissionVoteBody(req, res);
    if (response === void 0) return;
    const clientId = parseClientIdHeader(req, res);
    if (clientId === null) return;
    const fromLoopback = detectFromLoopback(req);
    const context = {
      ...clientId !== void 0 ? { clientId } : {},
      fromLoopback
    };
    let accepted;
    try {
      accepted = bridge.respondToPermission(requestId, response, context);
    } catch (err) {
      sendPermissionVoteError2(res, err, {
        route: "POST /permission/:requestId"
      });
      return;
    }
    if (!accepted) {
      res.status(404).json({ error: "No pending permission request", requestId });
      return;
    }
    res.status(200).json({});
  });
}
__name(registerPermissionRoutes, "registerPermissionRoutes");

// packages/cli/src/serve/routes/session.ts
init_esbuild_shims();
import * as crypto2 from "node:crypto";
import * as path11 from "node:path";

// packages/cli/src/serve/server/prompt-deadline.ts
init_esbuild_shims();
var PromptDeadlineExceededError = class extends Error {
  static {
    __name(this, "PromptDeadlineExceededError");
  }
  deadlineMs;
  constructor(deadlineMs) {
    super(`prompt exceeded the ${deadlineMs}ms deadline`);
    this.name = "PromptDeadlineExceededError";
    this.deadlineMs = deadlineMs;
  }
};
function resolvePromptDeadlineMs(serverMs, requestMs) {
  if (serverMs === void 0 || !Number.isFinite(serverMs) || serverMs <= 0) {
    return void 0;
  }
  if (requestMs === void 0 || !Number.isFinite(requestMs) || requestMs <= 0) {
    return serverMs;
  }
  return Math.min(serverMs, requestMs);
}
__name(resolvePromptDeadlineMs, "resolvePromptDeadlineMs");

// packages/cli/src/serve/server/session-list.ts
init_esbuild_shims();
var DEFAULT_SESSION_PAGE_SIZE = 20;
var MAX_SESSION_PAGE_SIZE = 100;
var InvalidCursorError = class extends Error {
  static {
    __name(this, "InvalidCursorError");
  }
  constructor(cursor) {
    super(`Invalid cursor: "${cursor}" is not a valid numeric cursor`);
    this.name = "InvalidCursorError";
  }
};
function parseSessionCursor(cursor) {
  if (cursor === "") return void 0;
  const trimmed = cursor.trim();
  const parsed = Number(trimmed);
  if (trimmed === "" || !Number.isFinite(parsed) || parsed < 0 || parsed > Number.MAX_SAFE_INTEGER) {
    throw new InvalidCursorError(cursor);
  }
  return parsed;
}
__name(parseSessionCursor, "parseSessionCursor");
async function listWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
  const rawSize = options?.size;
  const requestedSize = typeof rawSize === "number" && Number.isSafeInteger(rawSize) ? rawSize : DEFAULT_SESSION_PAGE_SIZE;
  const pageSize = Math.min(Math.max(requestedSize, 1), MAX_SESSION_PAGE_SIZE);
  let numericCursor;
  if (options?.cursor != null) {
    numericCursor = parseSessionCursor(options.cursor);
  }
  const isFirstPage = numericCursor === void 0;
  const sessionService = new SessionService(workspaceCwd);
  const archiveState = options?.archiveState ?? "active";
  const persisted = await sessionService.listSessions({
    cursor: numericCursor,
    size: pageSize,
    archiveState
  });
  const bySessionId = /* @__PURE__ */ new Map();
  for (const item of persisted.items) {
    bySessionId.set(item.sessionId, {
      sessionId: item.sessionId,
      workspaceCwd: item.cwd,
      createdAt: item.startTime,
      updatedAt: new Date(item.mtime).toISOString(),
      displayName: item.customTitle || item.prompt,
      clientCount: 0,
      hasActivePrompt: false,
      isArchived: item.isArchived === true
    });
  }
  if (archiveState === "archived") {
    const sessions2 = [...bySessionId.values()];
    const nextCursor2 = persisted.nextCursor != null ? String(persisted.nextCursor) : void 0;
    return { sessions: sessions2, nextCursor: nextCursor2 };
  }
  const liveSessions = bridge.listWorkspaceSessions(workspaceCwd);
  for (const live of liveSessions) {
    const existing = bySessionId.get(live.sessionId);
    if (existing) {
      bySessionId.set(live.sessionId, {
        ...existing,
        ...live,
        createdAt: existing.createdAt,
        displayName: live.displayName ?? existing.displayName,
        updatedAt: live.updatedAt ?? existing.updatedAt,
        clientCount: live.clientCount,
        hasActivePrompt: live.hasActivePrompt,
        isArchived: false
      });
    } else if (isFirstPage && !await sessionService.sessionExists(live.sessionId)) {
      bySessionId.set(live.sessionId, {
        ...live,
        createdAt: live.createdAt,
        clientCount: live.clientCount,
        hasActivePrompt: live.hasActivePrompt,
        isArchived: false
      });
    }
  }
  const sessions = [...bySessionId.values()].sort((a, b) => {
    const aTime = Date.parse(a.updatedAt ?? a.createdAt);
    const bTime = Date.parse(b.updatedAt ?? b.createdAt);
    return bTime - aTime;
  });
  const nextCursor = persisted.nextCursor != null ? String(persisted.nextCursor) : void 0;
  return { sessions, nextCursor };
}
__name(listWorkspaceSessionsForResponse, "listWorkspaceSessionsForResponse");
function parseSessionPageSizeQuery(raw) {
  if (typeof raw !== "string") return void 0;
  const trimmed = raw.trim();
  if (!/^[+-]?\d+$/.test(trimmed)) return void 0;
  const parsed = Number.parseInt(trimmed, 10);
  if (!Number.isFinite(parsed)) return void 0;
  if (Number.isSafeInteger(parsed)) return parsed;
  return trimmed.startsWith("-") ? 1 : MAX_SESSION_PAGE_SIZE;
}
__name(parseSessionPageSizeQuery, "parseSessionPageSizeQuery");

// packages/cli/src/serve/routes/session.ts
function registerSessionRoutes(app, deps) {
  const {
    boundWorkspace,
    bridge,
    archiveCoordinator,
    mutate,
    sendBridgeError: sendBridgeError2,
    daemonLog,
    promptDeadlineMs,
    sessionShellCommandEnabled
  } = deps;
  const LANGUAGE_CODES = deps.languageCodes;
  const parseSessionIdsBody = /* @__PURE__ */ __name((req, res) => {
    const body = safeBody(req);
    const sessionIds = body["sessionIds"];
    if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((id) => typeof id === "string")) {
      res.status(400).json({
        error: "`sessionIds` must be a non-empty string array (max 100)",
        code: "invalid_request"
      });
      return void 0;
    }
    return [...new Set(sessionIds)];
  }, "parseSessionIdsBody");
  const serializeSessionErrors = /* @__PURE__ */ __name((errors) => errors.map((e) => ({
    sessionId: e.sessionId,
    error: e.error instanceof Error ? e.error.message : String(e.error)
  })), "serializeSessionErrors");
  const withMutableSession = /* @__PURE__ */ __name((route, handler) => async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      await archiveCoordinator.runSharedMany([sessionId], async () => {
        await handler(req, res, sessionId);
      });
    } catch (err) {
      sendBridgeError2(res, err, { route, sessionId });
    }
  }, "withMutableSession");
  app.post("/session", mutate(), async (req, res) => {
    const body = safeBody(req);
    const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
    if (cwd === void 0) return;
    const modelServiceId = typeof body["modelServiceId"] === "string" ? body["modelServiceId"] : void 0;
    const rawSessionScope = body["sessionScope"];
    let sessionScope;
    if (rawSessionScope !== void 0) {
      if (rawSessionScope !== "single" && rawSessionScope !== "thread") {
        res.status(400).json({
          error: '`sessionScope` must be "single" or "thread" when provided',
          code: "invalid_session_scope"
        });
        return;
      }
      sessionScope = rawSessionScope;
    }
    const clientId = parseClientIdHeader(req, res);
    if (clientId === null) return;
    try {
      const session = await bridge.spawnOrAttach({
        workspaceCwd: cwd,
        modelServiceId,
        ...clientId !== void 0 ? { clientId } : {},
        ...sessionScope !== void 0 ? { sessionScope } : {}
      });
      if (daemonLog) {
        daemonLog.info(
          session.attached ? "session attached" : "session spawned",
          { sessionId: session.sessionId, clientId: session.clientId }
        );
      }
      if (!res.writable) {
        if (daemonLog) {
          daemonLog.warn(
            "session reaped (client disconnected before response)",
            {
              sessionId: session.sessionId,
              attached: session.attached
            }
          );
        }
        if (!session.attached) {
          bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
          });
        } else {
          bridge.detachClient(session.sessionId, session.clientId).catch(() => {
          });
        }
        return;
      }
      res.status(200).json(session);
    } catch (err) {
      sendBridgeError2(res, err, { route: "POST /session" });
    }
  });
  const restoreSessionHandler = /* @__PURE__ */ __name((action) => async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (!sessionId) return;
    const body = safeBody(req);
    const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
    if (cwd === void 0) return;
    const clientId = parseClientIdHeader(req, res);
    if (clientId === null) return;
    try {
      const session = await archiveCoordinator.runSharedMany(
        [sessionId],
        async () => {
          await assertSessionLoadable(cwd, sessionId);
          return action === "load" ? await bridge.loadSession({
            sessionId,
            workspaceCwd: cwd,
            ...clientId !== void 0 ? { clientId } : {}
          }) : await bridge.resumeSession({
            sessionId,
            workspaceCwd: cwd,
            ...clientId !== void 0 ? { clientId } : {}
          });
        }
      );
      if (daemonLog) {
        daemonLog.info(
          `session ${action}${session.attached ? " (attached)" : ""}`,
          { sessionId: session.sessionId, clientId: session.clientId }
        );
      }
      if (!res.writable) {
        if (!session.attached) {
          bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
          });
        } else {
          bridge.detachClient(session.sessionId, session.clientId).catch(() => {
          });
        }
        return;
      }
      res.status(200).json(session);
    } catch (err) {
      sendBridgeError2(res, err, {
        route: `POST /session/:id/${action}`,
        sessionId
      });
    }
  }, "restoreSessionHandler");
  app.post("/session/:id/load", mutate(), restoreSessionHandler("load"));
  app.post("/session/:id/resume", mutate(), restoreSessionHandler("resume"));
  app.post(
    "/session/:id/branch",
    mutate(),
    withMutableSession(
      "POST /session/:id/branch",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        let name = typeof body?.["name"] === "string" ? body["name"] : void 0;
        if (name) {
          name = name.replace(/[\x00-\x1F\x7F-\x9F]/g, "");
          if (name.length > 200) {
            name = name.slice(0, 200);
          }
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const result = await bridge.branchSession(
          sessionId,
          { name },
          { clientId }
        );
        if (!res.writable) {
          if (!result.attached) {
            bridge.killSession(result.sessionId, { requireZeroAttaches: true }).catch(() => {
            });
          } else {
            bridge.detachClient(result.sessionId, result.clientId).catch(() => {
            });
          }
          return;
        }
        res.status(201).json(result);
      }
    )
  );
  app.post(
    "/session/:id/fork",
    mutate(),
    withMutableSession(
      "POST /session/:id/fork",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        const directive = body["directive"];
        if (typeof directive !== "string" || directive.trim().length === 0) {
          res.status(400).json({
            error: "`directive` is required and must be a non-empty string",
            code: "missing_directive"
          });
          return;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const result = await bridge.launchSessionForkAgent(
          sessionId,
          directive,
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(202).json(result);
      }
    )
  );
  app.post(
    "/session/:id/cd",
    mutate(),
    withMutableSession("POST /session/:id/cd", async (req, res, sessionId) => {
      const body = safeBody(req);
      const targetPath = body["path"];
      if (typeof targetPath !== "string" || targetPath.length === 0 || !path11.isAbsolute(targetPath)) {
        res.status(400).json({
          error: "`path` is required and must be an absolute path",
          code: "invalid_path"
        });
        return;
      }
      const clientId = parseClientIdHeader(req, res);
      if (clientId === null) return;
      const result = await bridge.changeSessionCwd(
        sessionId,
        { path: targetPath },
        clientId !== void 0 ? { clientId } : void 0
      );
      res.status(200).json(result);
    })
  );
  app.get("/session/:id/status", (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(bridge.getSessionSummary(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/status",
        sessionId
      });
    }
  });
  app.get("/session/:id/context", async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(await bridge.getSessionContextStatus(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/context",
        sessionId
      });
    }
  });
  app.get("/session/:id/context-usage", async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(
        await bridge.getSessionContextUsageStatus(sessionId, {
          detail: req.query["detail"] === "true"
        })
      );
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/context-usage",
        sessionId
      });
    }
  });
  app.get("/session/:id/stats", async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(await bridge.getSessionStatsStatus(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/stats",
        sessionId
      });
    }
  });
  app.get("/session/:id/supported-commands", async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(await bridge.getSessionSupportedCommandsStatus(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/supported-commands",
        sessionId
      });
    }
  });
  app.get("/session/:id/tasks", async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(await bridge.getSessionTasksStatus(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/tasks",
        sessionId
      });
    }
  });
  app.get("/session/:id/lsp", async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(await bridge.getSessionLspStatus(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/lsp",
        sessionId
      });
    }
  });
  app.get("/session/:id/hooks", async (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    try {
      res.status(200).json(await bridge.getSessionHooksStatus(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /session/:id/hooks", sessionId });
    }
  });
  app.post(
    "/session/:id/tasks/:taskId/cancel",
    mutate({ strict: true }),
    withMutableSession(
      "POST /session/:id/tasks/:taskId/cancel",
      async (req, res, sessionId) => {
        const taskId = req.params["taskId"];
        if (!taskId) {
          res.status(400).json({
            error: "`taskId` route parameter is required"
          });
          return;
        }
        const body = safeBody(req);
        const kind = body["kind"];
        if (kind !== "agent" && kind !== "shell" && kind !== "monitor") {
          res.status(400).json({ error: '`kind` must be "agent", "shell", or "monitor"' });
          return;
        }
        res.status(200).json(await bridge.cancelSessionTask(sessionId, taskId, kind));
      }
    )
  );
  app.post(
    "/session/:id/goal/clear",
    mutate({ strict: true }),
    withMutableSession(
      "POST /session/:id/goal/clear",
      async (_req, res, sessionId) => {
        res.status(200).json(await bridge.clearSessionGoal(sessionId));
      }
    )
  );
  app.post(
    "/session/:id/continue",
    mutate({ strict: true }),
    withMutableSession(
      "POST /session/:id/continue",
      async (req, res, sessionId) => {
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const promptId = crypto2.randomUUID();
        res.status(200).json(
          await bridge.continueSession(sessionId, {
            ...clientId !== void 0 ? { clientId } : {},
            promptId
          })
        );
      }
    )
  );
  app.post(
    "/session/:id/prompt",
    mutate(),
    withMutableSession(
      "POST /session/:id/prompt",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        const prompt = body["prompt"];
        if (!Array.isArray(prompt) || prompt.length === 0) {
          res.status(400).json({
            error: "`prompt` is required and must be a non-empty array of content blocks"
          });
          return;
        }
        if (!prompt.every(
          (item) => typeof item === "object" && item !== null && !Array.isArray(item)
        )) {
          res.status(400).json({
            error: "each `prompt` element must be an object (content block)"
          });
          return;
        }
        const rawRequestDeadline = body["deadlineMs"];
        let requestDeadlineMs;
        if (rawRequestDeadline !== void 0 && rawRequestDeadline !== null) {
          if (typeof rawRequestDeadline !== "number" || !Number.isFinite(rawRequestDeadline) || !Number.isInteger(rawRequestDeadline) || rawRequestDeadline <= 0) {
            res.status(400).json({
              error: "`deadlineMs` must be a positive integer (milliseconds)",
              code: "invalid_deadline_ms"
            });
            return;
          }
          requestDeadlineMs = rawRequestDeadline;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const promptId = crypto2.randomUUID();
        const forwardedBody = { ...body };
        delete forwardedBody["deadlineMs"];
        const lastEventId = bridge.getSessionLastEventId(sessionId);
        addDaemonRequestAttribute("qwen-code.prompt_id", promptId);
        const abort = new AbortController();
        let responseFinished = false;
        const onResClose = /* @__PURE__ */ __name(() => {
          if (!responseFinished) abort.abort();
        }, "onResClose");
        const onResFinish = /* @__PURE__ */ __name(() => {
          responseFinished = true;
          res.off("close", onResClose);
        }, "onResFinish");
        res.once("close", onResClose);
        res.once("finish", onResFinish);
        const effectiveDeadlineMs = resolvePromptDeadlineMs(
          promptDeadlineMs,
          requestDeadlineMs
        );
        let deadlineTimer;
        if (effectiveDeadlineMs !== void 0) {
          deadlineTimer = setTimeout(() => {
            if (!abort.signal.aborted) {
              abort.abort(new PromptDeadlineExceededError(effectiveDeadlineMs));
            }
          }, effectiveDeadlineMs);
          deadlineTimer.unref();
        }
        let promptPromise;
        try {
          promptPromise = bridge.sendPrompt(
            sessionId,
            {
              ...forwardedBody,
              sessionId,
              prompt
            },
            abort.signal,
            {
              ...clientId !== void 0 ? { clientId } : {},
              promptId
            }
          );
        } catch (err) {
          if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
          res.off("close", onResClose);
          res.off("finish", onResFinish);
          if (daemonLog && err instanceof PromptQueueFullError) {
            daemonLog.warn("prompt admission rejected: queue full", {
              sessionId,
              promptId,
              ...clientId !== void 0 ? { clientId } : {},
              limit: err.limit,
              pendingCount: err.pendingCount
            });
          }
          if (daemonLog && err instanceof InvalidClientIdError) {
            daemonLog.warn("prompt admission rejected: invalid client id", {
              sessionId,
              promptId,
              ...clientId !== void 0 ? { clientId } : {}
            });
          }
          sendBridgeError2(res, err, {
            route: "POST /session/:id/prompt",
            sessionId
          });
          return;
        }
        res.off("close", onResClose);
        promptPromise.then(
          () => {
            if (daemonLog) {
              daemonLog.info("prompt turn completed", {
                sessionId,
                promptId,
                clientId
              });
            }
          },
          (err) => {
            if (daemonLog) {
              const errName = err instanceof Error ? err.name : void 0;
              daemonLog.warn(
                `prompt turn failed: ${errName ? `[${errName}] ` : ""}${err instanceof Error ? err.message : String(err)}`,
                { sessionId, promptId, clientId }
              );
            }
          }
        ).finally(() => {
          if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
        }).catch(() => {
        });
        if (daemonLog) {
          daemonLog.info("prompt enqueued", { sessionId, promptId, clientId });
        }
        res.status(202).json({ promptId, lastEventId });
      }
    )
  );
  app.post(
    "/session/:id/heartbeat",
    mutate(),
    withMutableSession("POST /session/:id/heartbeat", (req, res, sessionId) => {
      const clientId = parseClientIdHeader(req, res);
      if (clientId === null) return;
      const result = bridge.recordHeartbeat(
        sessionId,
        clientId !== void 0 ? { clientId } : void 0
      );
      res.status(200).json(result);
    })
  );
  app.post(
    "/session/:id/detach",
    mutate(),
    withMutableSession(
      "POST /session/:id/detach",
      async (req, res, sessionId) => {
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        await bridge.detachClient(sessionId, clientId);
        res.status(204).end();
      }
    )
  );
  app.post(
    "/session/:id/cancel",
    mutate(),
    withMutableSession(
      "POST /session/:id/cancel",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        await bridge.cancelSession(
          sessionId,
          {
            ...body,
            sessionId
          },
          clientId !== void 0 ? { clientId } : void 0
        );
        if (daemonLog) {
          daemonLog.info("cancel sent", { sessionId, clientId });
        }
        res.status(204).end();
      }
    )
  );
  app.delete("/session/:id", async (req, res) => {
    const sessionId = req.params["id"];
    const clientId = parseClientIdHeader(req, res);
    if (clientId === null) return;
    try {
      await archiveCoordinator.runExclusiveMany(
        [sessionId],
        async () => bridge.closeSession(
          sessionId,
          clientId !== void 0 ? { clientId } : void 0
        )
      );
      res.status(204).end();
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "DELETE /session/:id",
        sessionId
      });
    }
  });
  app.post("/sessions/delete", mutate(), async (req, res) => {
    const clientId = parseClientIdHeader(req, res);
    if (clientId === null) return;
    const uniqueIds = parseSessionIdsBody(req, res);
    if (uniqueIds === void 0) return;
    try {
      const service = new SessionService(boundWorkspace);
      const result = await deleteDaemonSessions({
        sessionIds: uniqueIds,
        service,
        bridge,
        coordinator: archiveCoordinator,
        onError: /* @__PURE__ */ __name(({ phase, sessionId, error: error2 }) => {
          writeStderrLine(
            `qwen serve: ${phase}Session failed for ${safeLogValue(sessionId)}: ${safeLogValue(error2)}`
          );
        }, "onError")
      });
      res.status(200).json(result);
    } catch (err) {
      sendBridgeError2(res, err, { route: "POST /sessions/delete" });
    }
  });
  app.post("/sessions/archive", mutate(), async (req, res) => {
    const uniqueIds = parseSessionIdsBody(req, res);
    if (uniqueIds === void 0) return;
    const service = new SessionService(boundWorkspace, {
      onWarning: logSessionArchiveWarning
    });
    try {
      const result = await archiveDaemonSessions({
        sessionIds: uniqueIds,
        service,
        bridge,
        coordinator: archiveCoordinator
      });
      res.status(200).json({
        archived: result.archived,
        alreadyArchived: result.alreadyArchived,
        notFound: result.notFound,
        errors: serializeSessionErrors(result.errors)
      });
    } catch (err) {
      sendBridgeError2(res, err, { route: "POST /sessions/archive" });
    }
  });
  app.post("/sessions/unarchive", mutate(), async (req, res) => {
    const uniqueIds = parseSessionIdsBody(req, res);
    if (uniqueIds === void 0) return;
    const service = new SessionService(boundWorkspace, {
      onWarning: logSessionArchiveWarning
    });
    try {
      const result = await unarchiveDaemonSessions({
        sessionIds: uniqueIds,
        service,
        coordinator: archiveCoordinator
      });
      res.status(200).json({
        unarchived: result.unarchived,
        alreadyActive: result.alreadyActive,
        notFound: result.notFound,
        errors: serializeSessionErrors(result.errors)
      });
    } catch (err) {
      sendBridgeError2(res, err, { route: "POST /sessions/unarchive" });
    }
  });
  app.patch(
    "/session/:id/metadata",
    withMutableSession("PATCH /session/:id/metadata", (req, res, sessionId) => {
      const body = safeBody(req);
      const clientId = parseClientIdHeader(req, res);
      if (clientId === null) return;
      const rawDisplayName = body["displayName"];
      if (rawDisplayName !== void 0 && typeof rawDisplayName !== "string") {
        res.status(400).json({
          error: "`displayName` must be a string",
          code: "invalid_metadata",
          field: "displayName"
        });
        return;
      }
      const displayName = typeof rawDisplayName === "string" ? rawDisplayName.slice(0, 256) : void 0;
      const effective = bridge.updateSessionMetadata(
        sessionId,
        { displayName },
        clientId !== void 0 ? { clientId } : void 0
      );
      res.status(200).json({ sessionId, ...effective });
    })
  );
  app.get("/workspace/:id/sessions", async (req, res) => {
    const workspaceCwd = req.params["id"] ?? "";
    if (!path11.isAbsolute(workspaceCwd)) {
      res.status(400).json({ error: "`:id` must decode to an absolute workspace path" });
      return;
    }
    const key = canonicalizeWorkspace(workspaceCwd);
    if (key !== boundWorkspace) {
      res.status(400).json({
        error: `Workspace mismatch: daemon is bound to "${boundWorkspace}"`,
        code: "workspace_mismatch",
        boundWorkspace,
        requestedWorkspace: key
      });
      return;
    }
    try {
      const cursor = typeof req.query["cursor"] === "string" ? req.query["cursor"] : void 0;
      const size = parseSessionPageSizeQuery(req.query["size"]);
      const rawArchiveState = req.query["archiveState"];
      let archiveState;
      if (rawArchiveState !== void 0) {
        if (typeof rawArchiveState !== "string" || rawArchiveState !== "active" && rawArchiveState !== "archived") {
          res.status(400).json({
            error: '`archiveState` must be "active" or "archived"',
            code: "invalid_archive_state"
          });
          return;
        }
        archiveState = rawArchiveState;
      }
      const result = await listWorkspaceSessionsForResponse(bridge, key, {
        cursor,
        size,
        archiveState
      });
      res.status(200).json({
        sessions: result.sessions,
        ...result.nextCursor != null ? { nextCursor: result.nextCursor } : {}
      });
    } catch (err) {
      if (err instanceof InvalidCursorError) {
        res.status(400).json({
          error: err.message,
          code: "invalid_cursor"
        });
        return;
      }
      writeStderrLine(
        `qwen serve: failed to list sessions for workspace ${safeLogValue(
          key
        )}: ${safeLogValue(err instanceof Error ? err.message : String(err))}`
      );
      res.status(500).json({
        error: "Failed to list sessions",
        code: "session_list_failed"
      });
    }
  });
  app.post(
    "/session/:id/model",
    mutate(),
    withMutableSession(
      "POST /session/:id/model",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        const modelId = body["modelId"];
        if (typeof modelId !== "string" || !modelId) {
          res.status(400).json({
            error: "`modelId` is required and must be a non-empty string"
          });
          return;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const response = await bridge.setSessionModel(
          sessionId,
          {
            ...body,
            sessionId,
            modelId
          },
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(200).json(response);
      }
    )
  );
  app.post(
    "/session/:id/recap",
    mutate(),
    withMutableSession(
      "POST /session/:id/recap",
      async (req, res, sessionId) => {
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const response = await bridge.generateSessionRecap(
          sessionId,
          clientId !== void 0 ? { clientId } : void 0
        );
        if (daemonLog) {
          const recap = response.recap;
          daemonLog.info(
            recap ? `recap generated len=${recap.length}` : "recap returned null",
            { sessionId, clientId }
          );
        }
        res.status(200).json(response);
      }
    )
  );
  app.post(
    "/session/:id/btw",
    mutate(),
    withMutableSession("POST /session/:id/btw", async (req, res, sessionId) => {
      const body = safeBody(req);
      const question = body["question"];
      if (typeof question !== "string" || question.trim().length === 0 || question.length > BTW_MAX_INPUT_LENGTH) {
        res.status(400).json({
          error: `\`question\` is required, must be a non-empty string, and at most ${BTW_MAX_INPUT_LENGTH} characters`
        });
        return;
      }
      const abort = new AbortController();
      const onResClose = /* @__PURE__ */ __name(() => {
        if (!res.writableEnded) abort.abort();
      }, "onResClose");
      res.once("close", onResClose);
      const clientId = parseClientIdHeader(req, res);
      if (clientId === null) {
        res.off("close", onResClose);
        return;
      }
      try {
        const result = await bridge.generateSessionBtw(
          sessionId,
          question.trim(),
          abort.signal,
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(200).json(result);
      } catch (err) {
        if (err instanceof DOMException && err.name === "AbortError" && abort.signal.aborted) {
          return;
        }
        sendBridgeError2(res, err, {
          route: "POST /session/:id/btw",
          sessionId
        });
      } finally {
        res.off("close", onResClose);
      }
    })
  );
  const MID_TURN_MESSAGE_MAX_LENGTH = 16 * 1024;
  app.post(
    "/session/:id/mid-turn-message",
    mutate(),
    withMutableSession(
      "POST /session/:id/mid-turn-message",
      (req, res, sessionId) => {
        const body = safeBody(req);
        const message = body["message"];
        const trimmed = typeof message === "string" ? message.trim() : "";
        if (trimmed.length === 0) {
          res.status(400).json({
            error: "`message` is required and must be a non-empty string"
          });
          return;
        }
        if (trimmed.length > MID_TURN_MESSAGE_MAX_LENGTH) {
          res.status(400).json({
            error: `\`message\` must be at most ${MID_TURN_MESSAGE_MAX_LENGTH} characters`
          });
          return;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const result = bridge.enqueueMidTurnMessage(
          sessionId,
          trimmed,
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(200).json(result);
      }
    )
  );
  app.get("/session/:id/pending-prompts", (req, res) => {
    const sessionId = requireSessionId(req, res);
    if (sessionId === null) return;
    const clientId = parseClientIdHeader(req, res);
    if (clientId === null) return;
    try {
      const pendingPrompts = bridge.getPendingPrompts(
        sessionId,
        clientId !== void 0 ? { clientId } : void 0
      );
      res.status(200).json({ pendingPrompts });
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/pending-prompts",
        sessionId
      });
    }
  });
  app.delete(
    "/session/:id/pending-prompts/:promptId",
    mutate(),
    withMutableSession(
      "DELETE /session/:id/pending-prompts/:promptId",
      (req, res, sessionId) => {
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const promptId = req.params["promptId"];
        if (!promptId) {
          res.status(400).json({ error: "`promptId` route parameter is required" });
          return;
        }
        const result = bridge.removePendingPrompt(
          sessionId,
          promptId,
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(200).json(result);
      }
    )
  );
  app.post(
    "/session/:id/shell",
    mutate({ strict: true }),
    withMutableSession(
      "POST /session/:id/shell",
      async (req, res, sessionId) => {
        if (!sessionShellCommandEnabled) {
          sendBridgeError2(res, new SessionShellDisabledError(), {
            route: "POST /session/:id/shell",
            sessionId
          });
          return;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) {
          return;
        }
        if (clientId === void 0) {
          sendBridgeError2(res, new SessionShellClientRequiredError(), {
            route: "POST /session/:id/shell",
            sessionId
          });
          return;
        }
        const body = safeBody(req);
        const command = body["command"];
        if (typeof command !== "string" || command.trim().length === 0) {
          res.status(400).json({
            error: "`command` is required and must be a non-empty string"
          });
          return;
        }
        const abort = new AbortController();
        const onResClose = /* @__PURE__ */ __name(() => {
          if (!res.writableEnded) abort.abort();
        }, "onResClose");
        res.once("close", onResClose);
        try {
          const result = await bridge.executeShellCommand(
            sessionId,
            command.trim(),
            abort.signal,
            { clientId }
          );
          if (daemonLog) {
            daemonLog.info("shell command completed", {
              sessionId,
              clientId,
              exitCode: result.exitCode
            });
          }
          res.status(200).json(result);
        } catch (err) {
          if (err instanceof DOMException && err.name === "AbortError" && abort.signal.aborted) {
            return;
          }
          sendBridgeError2(res, err, {
            route: "POST /session/:id/shell",
            sessionId
          });
        } finally {
          res.off("close", onResClose);
        }
      }
    )
  );
  app.get("/session/:id/rewind/snapshots", async (req, res) => {
    const sessionId = req.params["id"];
    if (!sessionId) {
      res.status(400).json({ error: "`sessionId` route parameter is required" });
      return;
    }
    try {
      res.status(200).json(await bridge.getRewindSnapshots(sessionId));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /session/:id/rewind/snapshots",
        sessionId
      });
    }
  });
  app.post(
    "/session/:id/rewind",
    mutate({ strict: true }),
    withMutableSession(
      "POST /session/:id/rewind",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        const promptId = body["promptId"];
        if (typeof promptId !== "string" || promptId.length === 0) {
          res.status(400).json({
            error: "`promptId` is required and must be a non-empty string",
            code: "missing_prompt_id"
          });
          return;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const response = await bridge.rewindSession(
          sessionId,
          { promptId, rewindFiles: body["rewindFiles"] !== false },
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(200).json(response);
      }
    )
  );
  app.post(
    "/session/:id/approval-mode",
    mutate({ strict: true }),
    withMutableSession(
      "POST /session/:id/approval-mode",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        const mode = body["mode"];
        const persist = body["persist"];
        if (typeof mode !== "string" || !APPROVAL_MODES.includes(mode)) {
          res.status(400).json({
            error: "`mode` is required and must be one of the allowed values",
            code: "invalid_approval_mode",
            allowed: APPROVAL_MODES
          });
          return;
        }
        if (persist !== void 0 && typeof persist !== "boolean") {
          res.status(400).json({
            error: "`persist` must be a boolean when provided",
            code: "invalid_persist_flag"
          });
          return;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const response = await bridge.setSessionApprovalMode(
          sessionId,
          mode,
          { persist: persist === true },
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(200).json(response);
      }
    )
  );
  app.post(
    "/session/:id/language",
    mutate(),
    withMutableSession(
      "POST /session/:id/language",
      async (req, res, sessionId) => {
        const body = safeBody(req);
        const language = body["language"];
        const syncOutputLanguage = body["syncOutputLanguage"];
        if (typeof language !== "string" || !LANGUAGE_CODES.includes(language)) {
          res.status(400).json({
            error: "`language` is required and must be one of: " + LANGUAGE_CODES.join(", "),
            code: "invalid_language",
            allowed: LANGUAGE_CODES
          });
          return;
        }
        if (syncOutputLanguage !== void 0 && typeof syncOutputLanguage !== "boolean") {
          res.status(400).json({
            error: "`syncOutputLanguage` must be a boolean when provided",
            code: "invalid_sync_flag"
          });
          return;
        }
        const clientId = parseClientIdHeader(req, res);
        if (clientId === null) return;
        const response = await bridge.setSessionLanguage(
          sessionId,
          {
            language,
            syncOutputLanguage: syncOutputLanguage === true
          },
          clientId !== void 0 ? { clientId } : void 0
        );
        res.status(200).json(response);
      }
    )
  );
}
__name(registerSessionRoutes, "registerSessionRoutes");

// packages/cli/src/serve/routes/workspace-status.ts
init_esbuild_shims();
function registerWorkspaceStatusRoutes(app, deps) {
  const { boundWorkspace, bridge, workspace, sendBridgeError: sendBridgeError2 } = deps;
  const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
  app.get("/workspace/mcp", async (_req, res) => {
    try {
      const ctx = buildWorkspaceCtx("GET /workspace/mcp");
      res.status(200).json(await workspace.getWorkspaceMcpStatus(ctx));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/mcp" });
    }
  });
  app.get("/workspace/mcp/:server/tools", async (req, res) => {
    const serverName = req.params["server"];
    if (!serverName || typeof serverName !== "string") {
      res.status(400).json({
        error: "Server name path parameter is required",
        code: "invalid_server_name"
      });
      return;
    }
    if (serverName.length > MAX_SERVER_NAME_LENGTH) {
      res.status(400).json({
        error: `Server name exceeds ${MAX_SERVER_NAME_LENGTH}-character limit`,
        code: "invalid_server_name"
      });
      return;
    }
    try {
      res.status(200).json(await bridge.getWorkspaceMcpToolsStatus(serverName));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/mcp/:server/tools" });
    }
  });
  app.get("/workspace/mcp/:server/resources", async (req, res) => {
    const serverName = req.params["server"];
    if (!serverName || typeof serverName !== "string") {
      res.status(400).json({
        error: "Server name path parameter is required",
        code: "invalid_server_name"
      });
      return;
    }
    if (serverName.length > MAX_SERVER_NAME_LENGTH) {
      res.status(400).json({
        error: `Server name exceeds ${MAX_SERVER_NAME_LENGTH}-character limit`,
        code: "invalid_server_name"
      });
      return;
    }
    try {
      res.status(200).json(await bridge.getWorkspaceMcpResourcesStatus(serverName));
    } catch (err) {
      sendBridgeError2(res, err, {
        route: "GET /workspace/mcp/:server/resources"
      });
    }
  });
  app.get("/workspace/skills", async (_req, res) => {
    try {
      const ctx = buildWorkspaceCtx("GET /workspace/skills");
      res.status(200).json(await workspace.getWorkspaceSkillsStatus(ctx));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/skills" });
    }
  });
  app.get("/workspace/tools", async (_req, res) => {
    try {
      res.status(200).json(await bridge.getWorkspaceToolsStatus());
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/tools" });
    }
  });
  app.get("/workspace/providers", async (_req, res) => {
    try {
      const ctx = buildWorkspaceCtx("GET /workspace/providers");
      res.status(200).json(await workspace.getWorkspaceProvidersStatus(ctx));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/providers" });
    }
  });
}
__name(registerWorkspaceStatusRoutes, "registerWorkspaceStatusRoutes");
function registerWorkspaceDiagnosticStatusRoutes(app, deps) {
  const { boundWorkspace, workspace, sendBridgeError: sendBridgeError2 } = deps;
  const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
  app.get("/workspace/env", async (_req, res) => {
    try {
      const ctx = buildWorkspaceCtx("GET /workspace/env");
      res.status(200).json(await workspace.getWorkspaceEnvStatus(ctx));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/env" });
    }
  });
  app.get("/workspace/preflight", async (_req, res) => {
    try {
      const ctx = buildWorkspaceCtx("GET /workspace/preflight");
      res.status(200).json(await workspace.getWorkspacePreflightStatus(ctx));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/preflight" });
    }
  });
  app.get("/workspace/hooks", async (_req, res) => {
    try {
      const ctx = buildWorkspaceCtx("GET /workspace/hooks");
      res.status(200).json(await workspace.getWorkspaceHooksStatus(ctx));
    } catch (err) {
      sendBridgeError2(res, err, { route: "GET /workspace/hooks" });
    }
  });
}
__name(registerWorkspaceDiagnosticStatusRoutes, "registerWorkspaceDiagnosticStatusRoutes");

// packages/cli/src/serve/routes/capabilities.ts
init_esbuild_shims();

// packages/cli/src/serve/server/serve-features.ts
init_esbuild_shims();
var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
var SERVE_LANGUAGE_CODES = [
  ...SUPPORTED_LANGUAGES.map((language) => language.code),
  "auto"
];
function advertisedMaxPendingPromptsPerSession(value) {
  if (value === void 0) return DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION;
  if (value === 0 || value === Number.POSITIVE_INFINITY) return null;
  return value;
}
__name(advertisedMaxPendingPromptsPerSession, "advertisedMaxPendingPromptsPerSession");
function createServeFeatures(deps) {
  const {
    opts,
    boundWorkspace,
    persistSettingAvailable,
    reloadAvailable,
    sessionShellCommandEnabled
  } = deps;
  let cachedVoiceTranscriptionAvailable;
  const invalidateServeFeaturesCache = /* @__PURE__ */ __name(() => {
    cachedVoiceTranscriptionAvailable = void 0;
  }, "invalidateServeFeaturesCache");
  const getCachedVoiceTranscriptionAvailable = /* @__PURE__ */ __name(() => {
    cachedVoiceTranscriptionAvailable ??= isWorkspaceVoiceTranscriptionAvailable(boundWorkspace);
    return cachedVoiceTranscriptionAvailable;
  }, "getCachedVoiceTranscriptionAvailable");
  return {
    languageCodes: SERVE_LANGUAGE_CODES,
    invalidateServeFeaturesCache,
    currentServeFeatures: /* @__PURE__ */ __name(() => getAdvertisedServeFeatures(void 0, {
      requireAuth: opts.requireAuth === true,
      mcpPoolActive: opts.mcpPoolActive !== false,
      allowOriginActive: opts.allowOrigins !== void 0 && opts.allowOrigins.length > 0,
      ...opts.promptDeadlineMs !== void 0 ? { promptDeadlineMs: opts.promptDeadlineMs } : {},
      ...opts.writerIdleTimeoutMs !== void 0 ? { writerIdleTimeoutMs: opts.writerIdleTimeoutMs } : {},
      persistSettingAvailable,
      sessionShellCommandEnabled,
      rateLimit: opts.rateLimit === true,
      reloadAvailable,
      clientMcpOverWsEnabled: opts.clientMcpOverWs === true,
      cdpTunnelOverWsEnabled: opts.cdpTunnelOverWs === true,
      voiceTranscriptionAvailable: getCachedVoiceTranscriptionAvailable(),
      // Advertised whenever the `/voice/stream` WS endpoint exists (ACP HTTP
      // on). A configured token no longer suppresses it — the browser carries
      // the bearer token via the WS subprotocol, which the upgrade listener
      // verifies (acp-http/index.ts).
      voiceWsAvailable: process.env["QWEN_SERVE_ACP_HTTP"] !== "0"
    }), "currentServeFeatures")
  };
}
__name(createServeFeatures, "createServeFeatures");
function isWorkspaceVoiceTranscriptionAvailable(boundWorkspace) {
  try {
    return hasConfiguredBatchVoiceTranscriptionModel(
      loadSettings(boundWorkspace)
    );
  } catch (err) {
    writeStderrLine(
      `qwen serve: workspace voice transcription capability check failed: ${err instanceof Error ? err.message : String(err)}`
    );
    return false;
  }
}
__name(isWorkspaceVoiceTranscriptionAvailable, "isWorkspaceVoiceTranscriptionAvailable");

// packages/cli/src/serve/routes/capabilities.ts
function registerCapabilitiesRoutes(app, deps) {
  app.get("/capabilities", (_req, res) => {
    const envelope = {
      v: CAPABILITIES_SCHEMA_VERSION,
      protocolVersions: getServeProtocolVersions(),
      ...deps.qwenCodeVersion ? { qwenCodeVersion: deps.qwenCodeVersion } : {},
      mode: deps.mode,
      features: deps.currentServeFeatures(),
      modelServices: [],
      // Surface the bound workspace so clients can detect mismatch pre-flight
      // and omit `cwd` on `POST /session`.
      workspaceCwd: deps.boundWorkspace,
      // Advertise supported transport families so SDK clients can
      // auto-negotiate the best available transport via negotiateTransport().
      transports: ["rest"],
      // Active mediation policy under the `policy` namespace.
      policy: { permission: deps.permissionPolicy },
      limits: {
        maxPendingPromptsPerSession: advertisedMaxPendingPromptsPerSession(
          deps.maxPendingPromptsPerSession
        )
      },
      supportedLanguages: deps.languageCodes
    };
    res.status(200).json(envelope);
  });
}
__name(registerCapabilitiesRoutes, "registerCapabilitiesRoutes");

// packages/cli/src/serve/routes/workspace-permissions.ts
init_esbuild_shims();
function registerWorkspacePermissionsRoutes(app, deps) {
  const {
    boundWorkspace,
    mutate,
    safeBody: safeBody2,
    workspace,
    parseAndValidateClientId
  } = deps;
  app.get("/workspace/permissions", (_req, res) => {
    try {
      res.status(200).json(buildPermissionSettings(loadSettings(boundWorkspace)));
    } catch (err) {
      writeStderrLine(
        `qwen serve: GET /workspace/permissions error: ${err instanceof Error ? err.message : String(err)}`
      );
      res.status(500).json({
        error: "Failed to load permission rules",
        code: "internal_error"
      });
    }
  });
  app.post(
    "/workspace/permissions",
    mutate({ strict: true }),
    async (req, res) => {
      const body = safeBody2(req);
      const scope = body["scope"];
      const ruleType = body["ruleType"];
      if (scope !== "user" && scope !== "workspace") {
        res.status(400).json({
          error: 'scope must be "user" or "workspace"',
          code: "invalid_scope"
        });
        return;
      }
      const permissionScope = scope;
      if (!isPermissionRuleType(ruleType)) {
        res.status(400).json({
          error: 'ruleType must be "allow", "ask", or "deny"',
          code: "invalid_rule_type"
        });
        return;
      }
      let rules;
      try {
        const settings = loadSettings(boundWorkspace);
        const scopeSettings = permissionScope === "workspace" ? settings.workspace.settings : settings.user.settings;
        const existingRules = readPermissionRuleSet(scopeSettings)[ruleType];
        rules = normalizePermissionRules(body["rules"], { existingRules });
      } catch (err) {
        if (err instanceof PermissionRulesValidationError) {
          res.status(400).json({
            error: err.message,
            code: err.code
          });
          return;
        }
        throw err;
      }
      const clientId = parseAndValidateClientId(req, res);
      if (clientId === null) return;
      const key = `permissions.${ruleType}`;
      let liveResponse;
      try {
        liveResponse = await workspace.setWorkspacePermissionRules(
          {
            route: "POST /workspace/permissions",
            workspaceCwd: boundWorkspace,
            ...clientId ? { originatorClientId: clientId } : {}
          },
          { scope: permissionScope, ruleType, rules }
        );
      } catch (err) {
        if (err instanceof WorkspacePermissionRulesSessionRequiredError) {
          res.status(409).json({
            error: "A live ACP session is required to update active permission rules.",
            code: "permission_session_required"
          });
          return;
        }
        writeStderrLine(
          `qwen serve: POST /workspace/permissions ACP error (key=${key}, scope=${permissionScope}, workspace=${boundWorkspace}): ${err instanceof Error ? err.message : String(err)}`
        );
        res.status(500).json({
          error: "Failed to update permission rules",
          code: "permission_update_failed"
        });
        return;
      }
      res.status(200).json(liveResponse);
    }
  );
}
__name(registerWorkspacePermissionsRoutes, "registerWorkspacePermissionsRoutes");

// packages/cli/src/serve/routes/workspace-settings.ts
init_esbuild_shims();
var TUI_ONLY_SETTINGS = /* @__PURE__ */ new Set([
  "general.vimMode",
  "general.terminalBell",
  "general.preferredEditor",
  "general.outputLanguage",
  "ide.enabled",
  "ui.showLineNumbers",
  "ui.renderMode",
  "ui.useTerminalBuffer",
  "ui.hideBanner",
  "ui.accessibility.enableLoadingPhrases",
  "ui.enableWelcomeBack"
]);
var WEB_SHELL_SETTINGS = /* @__PURE__ */ new Set(["ui.compactMode", "voiceModel"]);
var VALID_WRITE_SCOPES = /* @__PURE__ */ new Set(["workspace"]);
var SECURITY_SENSITIVE_SETTINGS = /* @__PURE__ */ new Set(["tools.approvalMode"]);
function getAllowedKeys() {
  const keys = new Set(
    getDialogSettingKeys().filter(
      (k) => !TUI_ONLY_SETTINGS.has(k) && !SECURITY_SENSITIVE_SETTINGS.has(k)
    )
  );
  for (const key of WEB_SHELL_SETTINGS) {
    keys.add(key);
  }
  return keys;
}
__name(getAllowedKeys, "getAllowedKeys");
function buildSettingsResponse(boundWorkspace, keys) {
  const loaded = loadSettings(boundWorkspace);
  const settings = [];
  for (const key of keys) {
    const def = getSettingDefinition(key);
    if (!def) continue;
    const effective = getNestedProperty(
      loaded.merged,
      key
    );
    const userVal = getNestedProperty(
      loaded.user.settings,
      key
    );
    const wsVal = getNestedProperty(
      loaded.workspace.settings,
      key
    );
    const values = {
      effective: effective !== void 0 ? effective : def.default
    };
    if (userVal !== void 0) values.user = userVal;
    if (wsVal !== void 0) values.workspace = wsVal;
    settings.push({
      key,
      type: def.type,
      label: def.label,
      category: def.category,
      ...def.description ? { description: def.description } : {},
      requiresRestart: def.requiresRestart,
      default: def.default,
      ...def.options?.length ? { options: def.options } : {},
      values
    });
  }
  const warnings = [];
  if (loaded.corruptedPath) {
    warnings.push({
      type: "corrupted",
      recovered: loaded.wasRecovered
    });
  }
  return {
    v: 1,
    ...warnings.length ? { warnings } : {},
    settings
  };
}
__name(buildSettingsResponse, "buildSettingsResponse");
var SCOPE_MAP = {
  workspace: "Workspace" /* Workspace */
};
function registerWorkspaceSettingsRoutes(app, deps) {
  const {
    boundWorkspace,
    mutate,
    safeBody: safeBody2,
    persistSetting,
    broadcastSettingsChanged,
    parseAndValidateClientId
  } = deps;
  const allowedKeys = getAllowedKeys();
  app.get("/workspace/settings", (_req, res) => {
    try {
      const response = buildSettingsResponse(boundWorkspace, allowedKeys);
      res.status(200).json(response);
    } catch (err) {
      writeStderrLine(
        `qwen serve: GET /workspace/settings error: ${err instanceof Error ? err.message : String(err)}`
      );
      res.status(500).json({
        error: "Failed to load settings",
        code: "internal_error"
      });
    }
  });
  app.post(
    "/workspace/settings",
    mutate({ strict: true }),
    async (req, res) => {
      const body = safeBody2(req);
      const scope = body["scope"];
      const key = body["key"];
      const value = body["value"];
      if (typeof scope !== "string" || !VALID_WRITE_SCOPES.has(scope)) {
        res.status(400).json({
          error: `scope must be one of: ${[...VALID_WRITE_SCOPES].join(", ")}`,
          code: "invalid_scope"
        });
        return;
      }
      if (typeof key !== "string" || !key) {
        res.status(400).json({
          error: "key is required and must be a string",
          code: "invalid_key"
        });
        return;
      }
      if (!allowedKeys.has(key)) {
        res.status(400).json({
          error: `Setting "${key}" is not modifiable via this API`,
          code: "disallowed_key"
        });
        return;
      }
      if (value === void 0 || value === null) {
        res.status(400).json({
          error: "value is required",
          code: "missing_value"
        });
        return;
      }
      const def = getSettingDefinition(key);
      if (!def) {
        res.status(400).json({
          error: `Unknown setting: ${key}`,
          code: "unknown_key"
        });
        return;
      }
      const validationError = validateSettingValue(def, value);
      if (validationError) {
        res.status(400).json({
          error: validationError,
          code: "invalid_value"
        });
        return;
      }
      const clientId = parseAndValidateClientId(req, res);
      if (clientId === null) return;
      try {
        const settingScope = SCOPE_MAP[scope];
        if (!settingScope) {
          res.status(400).json({
            error: `scope must be one of: ${[...VALID_WRITE_SCOPES].join(", ")}`,
            code: "invalid_scope"
          });
          return;
        }
        await persistSetting(boundWorkspace, settingScope, key, value);
      } catch (err) {
        writeStderrLine(
          `qwen serve: POST /workspace/settings persist error (key=${key}, scope=${scope}, workspace=${boundWorkspace}): ${err instanceof Error ? err.message : String(err)}`
        );
        res.status(500).json({
          error: "Failed to persist setting",
          code: "persist_error"
        });
        return;
      }
      try {
        broadcastSettingsChanged(key, value, scope, clientId);
      } catch (err) {
        writeStderrLine(
          `qwen serve: POST /workspace/settings broadcast error (key=${key}, scope=${scope}): ${err instanceof Error ? err.message : String(err)}`
        );
      }
      res.status(200).json({
        key,
        scope,
        value,
        requiresRestart: def.requiresRestart
      });
    }
  );
}
__name(registerWorkspaceSettingsRoutes, "registerWorkspaceSettingsRoutes");

// packages/cli/src/serve/routes/sse-events.ts
init_esbuild_shims();

// packages/cli/src/serve/server/error-response.ts
init_esbuild_shims();
function bridgeErrorExtraContext(ctx) {
  const extra = {};
  for (const [key, value] of Object.entries(ctx ?? {})) {
    if (key === "route" || key === "sessionId" || value === void 0) {
      continue;
    }
    extra[key] = value;
  }
  return extra;
}
__name(bridgeErrorExtraContext, "bridgeErrorExtraContext");
function sendPermissionVoteError(res, err, ctx, daemonLog) {
  if (err instanceof InvalidPermissionOptionError) {
    res.status(400).json({
      error: err.message,
      code: "invalid_option_id",
      requestId: err.requestId,
      optionId: err.optionId
    });
    return;
  }
  if (err instanceof PermissionForbiddenError) {
    res.status(403).json({
      error: err.message,
      code: "permission_forbidden",
      requestId: err.requestId,
      sessionId: err.sessionId,
      reason: err.reason
    });
    return;
  }
  if (err instanceof PermissionPolicyNotImplementedError) {
    res.status(501).json({
      error: err.message,
      code: "permission_policy_not_implemented",
      policy: err.policy
    });
    return;
  }
  if (err instanceof CancelSentinelCollisionError) {
    res.status(500).json({
      error: err.message,
      code: "cancel_sentinel_collision",
      requestId: err.requestId,
      sentinel: err.sentinel
    });
    return;
  }
  sendBridgeError(res, err, ctx, daemonLog);
}
__name(sendPermissionVoteError, "sendPermissionVoteError");
function sendBridgeError(res, err, ctx, daemonLog) {
  if (err instanceof WorkspaceInitConflictError) {
    res.status(409).json({
      error: err.message,
      code: "workspace_init_conflict",
      path: err.path,
      existingSize: err.existingSize
    });
    return;
  }
  if (err instanceof WorkspaceInitPathEscapeError) {
    res.status(400).json({
      error: err.message,
      code: "workspace_init_path_escape",
      filename: err.filename,
      boundWorkspace: err.boundWorkspace
    });
    return;
  }
  if (err instanceof WorkspaceInitSymlinkError) {
    res.status(400).json({
      error: err.message,
      code: "workspace_init_symlink",
      target: err.target,
      kind: err.kind
    });
    return;
  }
  if (err instanceof WorkspaceInitRaceError) {
    res.status(400).json({
      error: err.message,
      code: "workspace_init_race",
      target: err.target,
      kind: err.kind
    });
    return;
  }
  if (err instanceof McpServerNotFoundError) {
    res.status(404).json({
      error: err.message,
      code: "mcp_server_not_found",
      serverName: err.serverName
    });
    return;
  }
  if (err instanceof McpServerRestartFailedError) {
    res.status(502).json({
      error: err.message,
      code: "mcp_server_restart_failed",
      errorKind: "protocol_error",
      serverName: err.serverName,
      mcpStatus: err.mcpStatus
    });
    return;
  }
  if (err instanceof BranchWhilePromptActiveError) {
    res.status(409).json({
      error: err.message,
      code: "branch_while_prompt_active",
      sessionId: err.sessionId
    });
    return;
  }
  if (err instanceof CdWhilePromptActiveError) {
    res.status(409).json({
      error: err.message,
      code: "cd_while_prompt_active",
      sessionId: err.sessionId
    });
    return;
  }
  if (err instanceof TrustGateError) {
    res.status(403).json({
      error: err.message,
      code: "trust_gate",
      errorKind: "auth_env_error"
    });
    return;
  }
  if (err instanceof SessionNotFoundError) {
    res.status(404).json({ error: err.message, sessionId: err.sessionId });
    return;
  }
  if (err instanceof SessionArchivedError) {
    res.status(409).json({
      error: err.message,
      code: "session_archived",
      sessionId: err.sessionId
    });
    return;
  }
  if (err instanceof SessionConflictError) {
    res.status(409).json({
      error: err.message,
      code: "session_conflict",
      sessionId: err.sessionId
    });
    return;
  }
  if (err instanceof SessionArchivingError) {
    res.set("Retry-After", "5");
    res.status(409).json({
      error: err.message,
      code: "session_archiving",
      sessionId: err.sessionId
    });
    return;
  }
  if (err instanceof InvalidClientIdError) {
    res.status(400).json({
      error: err.message,
      code: "invalid_client_id",
      sessionId: err.sessionId,
      clientId: err.clientId
    });
    return;
  }
  if (err instanceof SessionShellDisabledError) {
    res.status(403).json({
      error: err.message,
      code: "session_shell_disabled",
      errorKind: "session_shell_disabled"
    });
    return;
  }
  if (err instanceof SessionShellClientRequiredError) {
    res.status(403).json({
      error: err.message,
      code: "client_id_required",
      errorKind: "client_id_required"
    });
    return;
  }
  if (err instanceof WorkspaceMismatchError) {
    writeStderrLine(
      `qwen serve: workspace_mismatch (POST /session): daemon bound to ${JSON.stringify(err.bound)}, rejected ${JSON.stringify(err.requested)}`
    );
    res.status(400).json({
      error: err.message,
      code: "workspace_mismatch",
      boundWorkspace: err.bound,
      requestedWorkspace: err.requested
    });
    return;
  }
  if (err instanceof InvalidSessionScopeError) {
    res.status(400).json({
      error: err.message,
      code: "invalid_session_scope"
    });
    return;
  }
  if (err instanceof InvalidSessionMetadataError) {
    res.status(400).json({
      error: err.message,
      code: "invalid_metadata",
      field: err.field
    });
    return;
  }
  if (err instanceof SessionLimitExceededError) {
    res.set("Retry-After", "5");
    res.status(503).json({
      error: err.message,
      code: "session_limit_exceeded",
      limit: err.limit
    });
    return;
  }
  if (err instanceof PromptQueueFullError) {
    res.set("Retry-After", "5");
    res.status(503).json({
      error: err.message,
      code: "prompt_queue_full",
      sessionId: err.sessionId,
      limit: err.limit,
      pendingCount: err.pendingCount
    });
    return;
  }
  if (err instanceof RestoreInProgressError) {
    res.set("Retry-After", "5");
    res.status(409).json({
      error: err.message,
      code: "restore_in_progress",
      sessionId: err.sessionId,
      activeAction: err.activeAction,
      requestedAction: err.requestedAction
    });
    return;
  }
  if (err instanceof SessionBusyError) {
    res.set("Retry-After", "5");
    res.status(409).json({
      error: err.message,
      code: "session_busy",
      sessionId: err.sessionId
    });
    return;
  }
  if (err instanceof InvalidRewindTargetError) {
    res.status(400).json({
      error: err.message,
      code: "invalid_rewind_target",
      sessionId: err.sessionId
    });
    return;
  }
  if (err && typeof err === "object") {
    const data = err.data;
    if (data && typeof data === "object") {
      const kind = data.errorKind;
      if (kind === "mcp_budget_would_exceed") {
        const d = data;
        res.status(409).json({
          error: errorMessage2(err),
          code: "mcp_budget_would_exceed",
          serverName: d.serverName
        });
        return;
      }
      if (kind === "mcp_server_spawn_failed") {
        const d = data;
        res.status(502).json({
          error: errorMessage2(err),
          code: "mcp_server_spawn_failed",
          serverName: d.serverName,
          exitCode: d.exitCode,
          stderr: d.stderr,
          ...d.timeout !== void 0 ? { timeout: d.timeout } : {}
        });
        return;
      }
      if (kind === "invalid_config") {
        const d = data;
        res.status(400).json({
          error: errorMessage2(err),
          code: "invalid_config",
          serverName: d.serverName,
          reason: d.reason
        });
        return;
      }
      if (kind === "acp_channel_unavailable") {
        res.status(503).json({
          error: errorMessage2(err),
          code: "acp_channel_unavailable"
        });
        return;
      }
      if (kind === "restrictive_sandbox") {
        res.status(403).json({
          error: errorMessage2(err),
          code: "restrictive_sandbox"
        });
        return;
      }
      if (kind === "directory_not_found") {
        const d = data;
        res.status(400).json({
          error: errorMessage2(err),
          code: "directory_not_found",
          path: d.path
        });
        return;
      }
      if (kind === "directory_not_trusted") {
        const d = data;
        res.status(403).json({
          error: errorMessage2(err),
          code: "directory_not_trusted",
          path: d.path
        });
        return;
      }
    }
  }
  recordDaemonBridgeError(err);
  const extraContext = bridgeErrorExtraContext(ctx);
  recordDaemonError(void 0, err, {
    ...ctx?.route ? { "http.route": ctx.route } : {},
    ...ctx?.sessionId ? { "session.id": ctx.sessionId } : {}
  });
  emitDaemonLog("Daemon bridge error.", {
    ...ctx?.route ? { "http.route": ctx.route } : {},
    ...ctx?.sessionId ? { "session.id": ctx.sessionId } : {},
    ...extraContext,
    "error.type": err instanceof Error ? err.name : typeof err,
    "error.message": (err instanceof Error ? err.message : String(err)).slice(
      0,
      1024
    )
  });
  if (daemonLog) {
    daemonLog.error(
      err instanceof Error ? err.message : String(err),
      err instanceof Error ? err : void 0,
      {
        ...ctx?.route ? { route: ctx.route } : {},
        ...ctx?.sessionId ? { sessionId: ctx.sessionId } : {},
        ...extraContext
      }
    );
  } else {
    const ctxParts = [
      ctx?.route,
      ctx?.sessionId ? `session=${ctx.sessionId}` : void 0,
      ...Object.entries(extraContext).map(([key, value]) => `${key}=${value}`)
    ].filter(Boolean);
    const ctxStr = ctxParts.length > 0 ? ` (${ctxParts.join(" ")})` : "";
    writeStderrLine(
      `qwen serve: bridge error${ctxStr}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
    );
  }
  res.status(500).json(errorPayload(err));
}
__name(sendBridgeError, "sendBridgeError");
function errorMessage2(err) {
  if (err instanceof Error) return err.message;
  if (err && typeof err === "object") {
    const maybe = err.message;
    if (typeof maybe === "string" && maybe.length > 0) return maybe;
    try {
      return JSON.stringify(err);
    } catch {
    }
  }
  return String(err);
}
__name(errorMessage2, "errorMessage");
function errorPayload(err) {
  const out = {
    error: errorMessage2(err)
  };
  if (err && typeof err === "object") {
    const obj = err;
    if ("code" in obj) out.code = obj["code"];
    if ("data" in obj) out.data = obj["data"];
  }
  return out;
}
__name(errorPayload, "errorPayload");

// packages/cli/src/serve/routes/sse-events.ts
var activeSseCount = 0;
function getActiveSseCount() {
  return activeSseCount;
}
__name(getActiveSseCount, "getActiveSseCount");
function formatSseFrame(event) {
  const existingMeta = event._meta;
  const existingServerTimestamp = existingMeta?.["serverTimestamp"];
  const serverTimestamp = typeof existingServerTimestamp === "number" && Number.isFinite(existingServerTimestamp) ? existingServerTimestamp : Date.now();
  const stamped = {
    ...event,
    _meta: { ...existingMeta ?? {}, serverTimestamp }
  };
  const dataJson = JSON.stringify(stamped);
  const idLine = "id" in event && event.id !== void 0 ? `id: ${event.id}
` : "";
  return `${idLine}event: ${event.type}
data: ${dataJson}

`;
}
__name(formatSseFrame, "formatSseFrame");
function registerSseEventsRoutes(app, deps) {
  const { bridge, daemonLog, sendBridgeError: sendBridgeError2, writerIdleTimeoutMs } = deps;
  app.get("/session/:id/events", (req, res) => {
    const sessionId = req.params["id"];
    const lastEventId = parseLastEventId(req.headers["last-event-id"]);
    const maxQueued = parseMaxQueuedQuery(req.query["maxQueued"], res);
    if (maxQueued === null) return;
    let iter;
    const abort = new AbortController();
    try {
      const snapshot = req.query["snapshot"] === "1";
      const iterable = bridge.subscribeEvents(sessionId, {
        signal: abort.signal,
        lastEventId,
        ...maxQueued !== void 0 ? { maxQueued } : {},
        ...snapshot ? { snapshot: true } : {}
      });
      iter = iterable[Symbol.asyncIterator]();
    } catch (err) {
      if (err instanceof SubscriberLimitExceededError) {
        writeStderrLine(
          `qwen serve: subscriber limit reached for session ${sessionId} (limit=${err.limit}); rejecting new SSE client with 429`
        );
        res.setHeader("Retry-After", "5");
        res.status(429).json({
          error: err.message,
          code: "subscriber_limit_exceeded",
          limit: err.limit
        });
        return;
      }
      sendBridgeError2(res, err, {
        route: "GET /session/:id/events",
        sessionId
      });
      return;
    }
    if (daemonLog) {
      const sseOpenedAt = Date.now();
      const sseClientId = req.headers["x-qwen-client-id"];
      daemonLog.info("SSE stream opened", { sessionId, clientId: sseClientId });
      res.on("close", () => {
        try {
          daemonLog.info("SSE stream closed", {
            sessionId,
            clientId: sseClientId,
            durationMs: Date.now() - sseOpenedAt
          });
        } catch {
        }
      });
    }
    res.status(200);
    res.setHeader("Content-Type", "text/event-stream");
    res.setHeader("Cache-Control", "no-cache, no-transform");
    res.setHeader("Connection", "keep-alive");
    res.setHeader("X-Accel-Buffering", "no");
    res.flushHeaders();
    activeSseCount++;
    let sseCounted = true;
    res.on("close", () => {
      if (sseCounted) {
        sseCounted = false;
        activeSseCount--;
      }
    });
    let writeChain = Promise.resolve();
    const trackWriterIdle = writerIdleTimeoutMs !== void 0 && writerIdleTimeoutMs > 0;
    let lastWriteAt = trackWriterIdle ? Date.now() : 0;
    const doWrite = /* @__PURE__ */ __name((chunk) => new Promise((resolve5, reject) => {
      if (res.writableEnded) {
        resolve5();
        return;
      }
      let ok;
      try {
        ok = res.write(chunk);
      } catch (err) {
        reject(err);
        return;
      }
      if (ok) {
        if (trackWriterIdle) lastWriteAt = Date.now();
        resolve5();
        return;
      }
      const onDrain = /* @__PURE__ */ __name(() => {
        res.off("close", onClose);
        res.off("error", onError);
        if (trackWriterIdle) lastWriteAt = Date.now();
        resolve5();
      }, "onDrain");
      const onClose = /* @__PURE__ */ __name(() => {
        res.off("drain", onDrain);
        res.off("error", onError);
        resolve5();
      }, "onClose");
      const onError = /* @__PURE__ */ __name((err) => {
        res.off("drain", onDrain);
        res.off("close", onClose);
        reject(err);
      }, "onError");
      res.once("drain", onDrain);
      res.once("close", onClose);
      res.once("error", onError);
    }), "doWrite");
    const writeWithBackpressure = /* @__PURE__ */ __name((chunk) => {
      const next = writeChain.then(() => doWrite(chunk));
      writeChain = next.catch(() => void 0);
      return next;
    }, "writeWithBackpressure");
    void writeWithBackpressure("retry: 3000\n\n").catch(() => {
    });
    const heartbeatTimer = setInterval(() => {
      if (!res.writableEnded) {
        void writeWithBackpressure(": heartbeat\n\n").catch(() => {
        });
      }
    }, 15e3);
    heartbeatTimer.unref();
    let idleTimer;
    const cleanup = /* @__PURE__ */ __name(() => {
      clearInterval(heartbeatTimer);
      if (idleTimer !== void 0) clearInterval(idleTimer);
      abort.abort();
    }, "cleanup");
    if (trackWriterIdle) {
      const writerIdleTimeoutMsValue = writerIdleTimeoutMs;
      const checkIntervalMs = Math.max(
        250,
        Math.min(5e3, Math.floor(writerIdleTimeoutMsValue / 4))
      );
      idleTimer = setInterval(() => {
        if (res.writableEnded) return;
        const idleForMs = Date.now() - lastWriteAt;
        if (idleForMs < writerIdleTimeoutMsValue) return;
        try {
          res.write(
            formatSseFrame({
              v: 1,
              type: "client_evicted",
              data: {
                reason: "writer_idle_timeout",
                errorKind: "writer_idle_timeout",
                idleForMs,
                timeoutMs: writerIdleTimeoutMsValue
              }
            })
          );
        } catch {
        }
        try {
          writeStderrLine(
            `qwen serve: evicting SSE client (session ${sessionId}) \u2014 writer idle for ${idleForMs}ms > ${writerIdleTimeoutMsValue}ms timeout`
          );
        } catch {
        }
        cleanup();
        try {
          if (!res.writableEnded) res.end();
        } catch {
        }
      }, checkIntervalMs);
      idleTimer.unref();
    }
    req.on("close", cleanup);
    res.on("error", (err) => {
      writeStderrLine(
        `qwen serve: SSE socket error (session ${sessionId}): ${err.message}`
      );
      cleanup();
    });
    void (async () => {
      try {
        while (true) {
          const next = await iter.next();
          if (next.done) break;
          if (res.writableEnded) break;
          if (next.value.type === "state_resync_required") {
            const data = next.value.data;
            const gap = typeof data.earliestAvailableId === "number" && typeof data.lastDeliveredId === "number" ? data.earliestAvailableId - data.lastDeliveredId - 1 : void 0;
            writeStderrLine(
              `qwen serve: SSE ring eviction detected (session ${sessionId}): lastEventId=${data.lastDeliveredId ?? "?"}, earliestInRing=${data.earliestAvailableId ?? "?"}, gap=${gap ?? "?"} events, reason=${data.reason ?? "?"}. Consumer must call loadSession to recover.`
            );
          }
          await writeWithBackpressure(formatSseFrame(next.value));
        }
      } catch (err) {
        if (!res.writableEnded) {
          const errorKind = mapDomainErrorToErrorKind(err);
          writeStderrLine(
            `qwen serve: bridge iterator error (session ${sessionId}): ${errorMessage2(err)}` + (errorKind ? ` [${errorKind}]` : "")
          );
          await writeWithBackpressure(
            formatSseFrame({
              v: 1,
              type: "stream_error",
              data: {
                error: errorMessage2(err),
                ...errorKind ? { errorKind } : {}
              }
            })
          ).catch(() => {
          });
        }
      } finally {
        cleanup();
        if (!res.writableEnded) res.end();
      }
    })();
  });
}
__name(registerSseEventsRoutes, "registerSseEventsRoutes");

// packages/cli/src/serve/routes/a2ui-action.ts
init_esbuild_shims();
import * as fsp4 from "node:fs/promises";
import * as path12 from "node:path";
var A2UI_MIME = "application/a2ui+json";
var ACTION_TOOL = "action";
var CALL_TIMEOUT_MS = 15e3;
function usableServerConfig(cfg) {
  if (!cfg) return false;
  if (typeof cfg.httpUrl === "string") {
    try {
      const parsed = new URL(cfg.httpUrl);
      return parsed.protocol === "http:" || parsed.protocol === "https:";
    } catch {
      return false;
    }
  }
  return typeof cfg.command === "string" && cfg.command.trim().length > 0;
}
__name(usableServerConfig, "usableServerConfig");
async function findFromSettingsFile(workspaceCwd) {
  try {
    const raw = await fsp4.readFile(
      path12.join(workspaceCwd, ".qwen", "settings.json"),
      "utf8"
    );
    const settings = JSON.parse(raw);
    for (const [name, cfg] of Object.entries(settings.mcpServers ?? {})) {
      if (name.toLowerCase().includes("a2ui") && usableServerConfig(cfg)) {
        return cfg;
      }
    }
  } catch {
  }
  return null;
}
__name(findFromSettingsFile, "findFromSettingsFile");
function buildTransport(cfg) {
  if (typeof cfg.httpUrl === "string") {
    return new StreamableHTTPClientTransport(new URL(cfg.httpUrl));
  }
  return new StdioClientTransport({
    command: cfg.command,
    args: cfg.args ?? [],
    // spawn() treats `env` as a complete replacement, not a merge — a partial
    // env (e.g. {API_KEY}) would strip PATH/HOME and break the child. Merge
    // over process.env like packages/core/src/tools/mcp-client.ts does; when
    // unset, let the SDK apply its safe default environment.
    ...cfg.env ? { env: { ...process.env, ...cfg.env } } : {},
    cwd: cfg.cwd
  });
}
__name(buildTransport, "buildTransport");
function extractA2uiActionResult(result) {
  if (result.isError) {
    const errMsg2 = (result.content ?? []).filter(
      (b) => b.type === "text" && typeof b.text === "string"
    ).map((b) => b.text).join("");
    throw new Error(
      `a2ui action tool returned error: ${errMsg2 || "unknown error"}`
    );
  }
  let commands = null;
  let fallback = "";
  for (const block of result.content ?? []) {
    if (commands === null && block.type === "resource" && block.resource?.mimeType === A2UI_MIME && typeof block.resource.text === "string") {
      try {
        const parsed = JSON.parse(block.resource.text);
        if (Array.isArray(parsed)) commands = parsed;
      } catch {
      }
    } else if (block.type === "text" && typeof block.text === "string") {
      fallback += block.text;
    }
  }
  return { commands, fallback };
}
__name(extractA2uiActionResult, "extractA2uiActionResult");
async function callA2uiAction(cfg, args) {
  const transport = buildTransport(cfg);
  const client = new Client({ name: "qwen-serve-a2ui", version: "0.0.1" });
  try {
    await client.connect(transport, { timeout: CALL_TIMEOUT_MS });
    const result = await client.callTool(
      { name: ACTION_TOOL, arguments: { ...args } },
      void 0,
      { timeout: CALL_TIMEOUT_MS }
    );
    return extractA2uiActionResult(result);
  } finally {
    await transport.close().catch(() => {
    });
    await client.close().catch(() => {
    });
  }
}
__name(callA2uiAction, "callA2uiAction");
function registerA2uiActionRoutes(app, opts) {
  const { boundWorkspace, mutate, safeBody: safeBody2, getMcpServers } = opts;
  const callAction = opts.callAction ?? callA2uiAction;
  app.post(
    "/session/:id/a2ui-action",
    mutate(),
    async (req, res) => {
      const body = safeBody2(req);
      const name = body["name"];
      if (typeof name !== "string" || name.trim().length === 0) {
        res.status(400).json({ error: "`name` is required" });
        return;
      }
      const surfaceId = typeof body["surfaceId"] === "string" ? body["surfaceId"] : void 0;
      const context = body["context"] && typeof body["context"] === "object" && !Array.isArray(body["context"]) ? body["context"] : void 0;
      let cfg = null;
      try {
        const servers = (await getMcpServers()).filter(
          (s) => s.name.toLowerCase().includes("a2ui") && usableServerConfig(s.config)
        );
        const live = servers.find((s) => s.mcpStatus === "connected");
        cfg = (live ?? servers[0])?.config ?? null;
      } catch {
      }
      if (!cfg) cfg = await findFromSettingsFile(boundWorkspace);
      if (!cfg) {
        res.status(503).json({
          error: "no a2ui MCP server found (neither runtime-registered nor in workspace settings mcpServers)"
        });
        return;
      }
      try {
        const { commands, fallback } = await callAction(cfg, {
          name: name.trim(),
          surfaceId,
          context
        });
        res.status(200).json({ commands, fallback });
      } catch (err) {
        writeStderrLine(
          `a2ui-action proxy failed: ${err instanceof Error ? err.message : String(err)}`
        );
        res.status(502).json({ error: "a2ui action call failed" });
      }
    }
  );
}
__name(registerA2uiActionRoutes, "registerA2uiActionRoutes");

// packages/cli/src/serve/server/fs-factory.ts
init_esbuild_shims();
function createDefaultFsAuditEmit() {
  const WARN_EVERY = 100;
  let droppedCount = 0;
  return (event) => {
    droppedCount += 1;
    if (droppedCount === 1 || droppedCount % WARN_EVERY === 0) {
      const data = event.data;
      const ctx = [];
      if (data?.errorKind) ctx.push(`errorKind=${data.errorKind}`);
      if (data?.intent) ctx.push(`intent=${data.intent}`);
      if (data?.pathHash) ctx.push(`pathHash=${data.pathHash}`);
      const ctxStr = ctx.length > 0 ? ` (${ctx.join(" ")})` : "";
      writeStderrLine(
        `qwen serve: fs audit emit is the default no-op \u2014 ${droppedCount} event(s) dropped so far. Latest type=${event.type}${ctxStr}. Inject deps.fsFactory in createServeApp to wire audit into the EventBus.`
      );
    }
  };
}
__name(createDefaultFsAuditEmit, "createDefaultFsAuditEmit");
function resolveBridgeFsFactory(input) {
  if (input.injected) return input.injected;
  return createWorkspaceFileSystemFactory({
    boundWorkspace: input.boundWorkspace,
    trusted: input.trusted,
    emit: input.emit ?? createDefaultFsAuditEmit(),
    ...input.customIgnoreFiles !== void 0 ? { customIgnoreFiles: input.customIgnoreFiles } : {}
  });
}
__name(resolveBridgeFsFactory, "resolveBridgeFsFactory");

// packages/cli/src/serve/server/telemetry.ts
init_esbuild_shims();
function resolveDaemonTelemetryRoute(req) {
  const path13 = req.path.replace(/\/$/, "") || "/";
  if (req.method === "POST" && path13 === "/session") {
    return { route: "POST /session" };
  }
  if (req.method === "POST" && path13 === "/sessions/delete") {
    return { route: "POST /sessions/delete" };
  }
  if (req.method === "GET" && path13 === "/daemon/status") {
    return { route: "GET /daemon/status" };
  }
  const sessionAction = path13.match(
    /^\/session\/([^/]+)\/(load|resume|prompt|cancel|recap|btw|mid-turn-message|model|shell|detach|rewind|approval-mode|language|a2ui-action)$/
  );
  const sessionActionId = sessionAction?.[1];
  const sessionActionName = sessionAction?.[2];
  if (sessionActionId && sessionActionName && req.method === "POST") {
    return {
      route: `POST /session/:id/${sessionActionName}`,
      sessionId: sessionActionId
    };
  }
  const sessionMetadata = path13.match(/^\/session\/([^/]+)\/metadata$/);
  if (sessionMetadata?.[1] && req.method === "PATCH") {
    return {
      route: "PATCH /session/:id/metadata",
      sessionId: sessionMetadata[1]
    };
  }
  const sessionPermission = path13.match(
    /^\/session\/([^/]+)\/permission\/([^/]+)$/
  );
  if (sessionPermission?.[1] && sessionPermission?.[2] && req.method === "POST") {
    const rawRequestId = sessionPermission[2];
    return {
      route: "POST /session/:id/permission/:requestId",
      sessionId: sessionPermission[1],
      ...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
    };
  }
  const globalPermission = path13.match(/^\/permission\/([^/]+)$/);
  if (globalPermission?.[1] && req.method === "POST") {
    const rawRequestId = globalPermission[1];
    return {
      route: "POST /permission/:requestId",
      ...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
    };
  }
  const deleteSession = path13.match(/^\/session\/([^/]+)$/);
  const deleteSessionId = deleteSession?.[1];
  if (deleteSessionId && req.method === "DELETE") {
    return { route: "DELETE /session/:id", sessionId: deleteSessionId };
  }
  if (req.method === "GET" && /^\/workspace\/[^/]+\/sessions$/.test(path13)) {
    return { route: "GET /workspace/:id/sessions" };
  }
  if (req.method === "POST" && path13 === "/workspace/init") {
    return { route: "POST /workspace/init" };
  }
  if (req.method === "POST" && path13 === "/workspace/setup-github") {
    return { route: "POST /workspace/setup-github" };
  }
  if (req.method === "POST" && path13 === "/workspace/reload") {
    return { route: "POST /workspace/reload" };
  }
  const mcpRestart = path13.match(/^\/workspace\/mcp\/([^/]+)\/restart$/);
  if (mcpRestart?.[1] && req.method === "POST") {
    return { route: "POST /workspace/mcp/:server/restart" };
  }
  if (req.method === "POST" && path13 === "/workspace/mcp/servers") {
    return { route: "POST /workspace/mcp/servers" };
  }
  const mcpDelete = path13.match(/^\/workspace\/mcp\/servers\/([^/]+)$/);
  if (mcpDelete?.[1] && req.method === "DELETE") {
    return { route: "DELETE /workspace/mcp/servers/:name" };
  }
  if (req.method === "POST" && path13 === "/workspace/auth/device-flow") {
    return { route: "POST /workspace/auth/device-flow" };
  }
  const deviceFlowDelete = path13.match(
    /^\/workspace\/auth\/device-flow\/([^/]+)$/
  );
  if (deviceFlowDelete?.[1] && req.method === "DELETE") {
    return { route: "DELETE /workspace/auth/device-flow/:id" };
  }
  const toolEnable = path13.match(/^\/workspace\/tools\/([^/]+)\/enable$/);
  if (toolEnable?.[1] && req.method === "POST") {
    return { route: "POST /workspace/tools/:name/enable" };
  }
  if (path13 === "/workspace/settings") {
    if (req.method === "GET") return { route: "GET /workspace/settings" };
    if (req.method === "POST") return { route: "POST /workspace/settings" };
  }
  if (path13 === "/workspace/permissions") {
    if (req.method === "GET") return { route: "GET /workspace/permissions" };
    if (req.method === "POST") return { route: "POST /workspace/permissions" };
  }
  if (path13 === "/workspace/trust") {
    if (req.method === "GET") return { route: "GET /workspace/trust" };
  }
  if (req.method === "POST" && path13 === "/workspace/trust/request") {
    return { route: "POST /workspace/trust/request" };
  }
  if (path13 === "/workspace/voice") {
    if (req.method === "GET") return { route: "GET /workspace/voice" };
    if (req.method === "POST") return { route: "POST /workspace/voice" };
  }
  if (req.method === "POST" && path13 === "/workspace/voice/transcribe") {
    return { route: "POST /workspace/voice/transcribe" };
  }
  return void 0;
}
__name(resolveDaemonTelemetryRoute, "resolveDaemonTelemetryRoute");
function daemonTelemetryMiddleware(boundWorkspace) {
  const workspaceHash = hashDaemonWorkspace(boundWorkspace);
  return (req, res, next) => {
    const route = resolveDaemonTelemetryRoute(req);
    if (!route) {
      next();
      return;
    }
    const rawClientId = req.get(CLIENT_ID_HEADER);
    const clientId = rawClientId !== void 0 && rawClientId !== "" && rawClientId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawClientId) ? rawClientId : void 0;
    const startMs = Date.now();
    void withDaemonRequestSpan(
      {
        method: req.method,
        route: route.route,
        workspaceHash,
        ...route.sessionId ? { sessionId: route.sessionId } : {},
        ...route.permissionRequestId ? { permissionRequestId: route.permissionRequestId } : {},
        ...clientId ? { clientId } : {}
      },
      async (span) => await new Promise((resolve5, reject) => {
        let done = false;
        const finish = /* @__PURE__ */ __name(() => {
          if (done) return;
          done = true;
          recordDaemonHttpResponse(span, res.statusCode);
          recordDaemonHttpRequest(
            Date.now() - startMs,
            route.route,
            res.statusCode
          );
          resolve5();
        }, "finish");
        res.once("finish", finish);
        res.once("close", finish);
        try {
          next();
        } catch (error2) {
          recordDaemonError(span, error2);
          reject(error2);
        }
      })
    ).catch(next);
  };
}
__name(daemonTelemetryMiddleware, "daemonTelemetryMiddleware");

// packages/cli/src/serve/server/access-log.ts
init_esbuild_shims();
var SESSION_ID_RE = /\/session\/([^/]+)/;
function installAccessLogMiddleware(app, daemonLog) {
  if (!daemonLog) return;
  app.use((req, res, next) => {
    const { method, path: reqPath } = req;
    if (method === "GET" && reqPath === "/health" || method === "POST" && reqPath.endsWith("/heartbeat")) {
      return next();
    }
    const startMs = Date.now();
    res.on("finish", () => {
      try {
        const status = res.statusCode;
        if (method === "GET" && reqPath.endsWith("/events") && status === 200) {
          return;
        }
        const durationMs = Date.now() - startMs;
        const sessionMatch = reqPath.match(SESSION_ID_RE);
        const sessionId = sessionMatch?.[1];
        const clientId = req.headers["x-qwen-client-id"];
        const ctx = {
          route: `${method} ${reqPath}`,
          ...sessionId ? { sessionId } : {},
          ...clientId ? { clientId } : {},
          status,
          durationMs
        };
        if (status >= 400) {
          daemonLog.warn("request completed", ctx);
        } else {
          daemonLog.info("request completed", ctx);
        }
      } catch {
      }
    });
    next();
  });
}
__name(installAccessLogMiddleware, "installAccessLogMiddleware");

// packages/cli/src/serve/server/device-flow-registry.ts
init_esbuild_shims();

// packages/cli/src/serve/auth/qwen-device-flow-provider.ts
init_esbuild_shims();
var QWEN_OAUTH_SCOPE = "openid profile email model.completion";
var STDERR_DETAIL_MAX = 2048;
function truncateForStderr(detail) {
  if (detail.length <= STDERR_DETAIL_MAX) return detail;
  const dropped = detail.length - STDERR_DETAIL_MAX;
  return `${detail.slice(0, STDERR_DETAIL_MAX)}\u2026[+${dropped} bytes truncated]`;
}
__name(truncateForStderr, "truncateForStderr");
var QwenOAuthDeviceFlowProvider = class {
  static {
    __name(this, "QwenOAuthDeviceFlowProvider");
  }
  providerId = "qwen-oauth";
  client;
  constructor(client) {
    this.client = client ?? new QwenOAuth2Client();
  }
  async start(opts) {
    const { code_verifier, code_challenge } = generatePKCEPair();
    let auth;
    try {
      auth = await this.client.requestDeviceAuthorization(
        {
          scope: QWEN_OAUTH_SCOPE,
          code_challenge,
          code_challenge_method: "S256"
        },
        { signal: opts.signal }
      );
    } catch (err) {
      const detail = err instanceof Error ? err.message : String(err);
      writeStderrLine(
        `[serve] qwen device-flow start failed (raw): ${truncateForStderr(detail)}`
      );
      throw new UpstreamDeviceFlowError(
        "Qwen IdP device authorization request failed"
      );
    }
    if (opts.signal.aborted) {
      throw new UpstreamDeviceFlowError("device-flow start aborted");
    }
    if (!isDeviceAuthorizationSuccess(auth)) {
      const errorData = auth;
      writeStderrLine(
        truncateForStderr(
          `[serve] qwen device-flow start error envelope (raw): error=${errorData?.error ?? "unknown"} description=${errorData?.error_description ?? "(none)"}`
        )
      );
      throw new UpstreamDeviceFlowError(
        "Qwen IdP rejected the device authorization request"
      );
    }
    return {
      deviceCode: brandSecret(auth.device_code),
      pkceVerifier: brandSecret(code_verifier),
      userCode: auth.user_code,
      verificationUri: auth.verification_uri,
      verificationUriComplete: auth.verification_uri_complete,
      expiresIn: auth.expires_in
      // Qwen IdP doesn't return `interval`; registry falls back to the
      // RFC 8628 default (5s) when this is undefined.
    };
  }
  async poll(state, opts) {
    if (!state.pkceVerifier) {
      return {
        kind: "error",
        errorKind: "invalid_grant",
        hint: "Qwen device-flow requires a PKCE verifier"
      };
    }
    if (opts.signal.aborted) {
      return { kind: "pending" };
    }
    let response;
    try {
      response = await this.client.pollDeviceToken(
        {
          device_code: unsafeRevealSecret(state.deviceCode),
          code_verifier: unsafeRevealSecret(state.pkceVerifier)
        },
        { signal: opts.signal }
      );
    } catch (err) {
      const errorKind = err instanceof QwenOAuthPollError ? mapRfc8628OAuthCode(err.oauthError) : "upstream_error";
      const aborted = opts.signal.aborted;
      if (!aborted) {
        let safeDetail;
        if (err instanceof QwenOAuthPollError) {
          const rawOauthError = err.oauthError ?? "(missing)";
          safeDetail = `oauthError=${sanitizeForStderr(rawOauthError)}`;
        } else if (err instanceof Error) {
          safeDetail = `${sanitizeForStderr(err.name)} (message ${err.message.length} bytes; raw suppressed to avoid echoing device_code/PKCE)`;
        } else {
          safeDetail = `<non-Error throw: ${typeof err}>`;
        }
        writeStderrLine(
          `[serve] qwen device-flow poll failed (errorKind=${errorKind}): ${truncateForStderr(safeDetail)}`
        );
      }
      return {
        kind: "error",
        errorKind,
        hint: errorKind === "upstream_error" ? "unexpected response from identity provider" : `Qwen IdP returned ${errorKind}`
      };
    }
    if (isDeviceTokenSuccess(response)) {
      const tokenData = response;
      const credentials = {
        access_token: tokenData.access_token,
        refresh_token: tokenData.refresh_token ?? void 0,
        token_type: tokenData.token_type,
        resource_url: tokenData.resource_url,
        expiry_date: tokenData.expires_in ? Date.now() + tokenData.expires_in * 1e3 : void 0
      };
      const expiresAt = credentials.expiry_date;
      const client = this.client;
      return {
        kind: "success",
        // `persist({signal})`
        // is now threaded end-to-end. The registry passes its
        // per-entry `cancelController.signal`; we forward it to
        // `cacheQwenCredentials({signal})` which forwards to
        // `fs.writeFile(..., {signal})`. A wedged disk write aborts
        // immediately when `cancel()` / `dispose()` / the
        // 30s `DEVICE_FLOW_PERSIST_TIMEOUT_MS` fires, instead of
        // hanging until the OS-level timeout.
        async persist(persistOpts) {
          await cacheQwenCredentials(credentials, {
            signal: persistOpts.signal
          });
          try {
            client.setCredentials(credentials);
          } catch {
          }
          return { expiresAt };
        }
        // `unpersist` was removed in favor of honoring the IdP's
        // already-completed approval over a microsecond cancel/dispose
        // race.
      };
    }
    if (isDeviceTokenPending(response)) {
      const pending = response;
      return pending.slowDown ? { kind: "slow_down" } : { kind: "pending" };
    }
    return {
      kind: "error",
      errorKind: "upstream_error",
      hint: "unexpected response from identity provider"
    };
  }
};
function mapRfc8628OAuthCode(code) {
  switch (code) {
    case "expired_token":
      return "expired_token";
    case "access_denied":
      return "access_denied";
    case "invalid_grant":
      return "invalid_grant";
    default:
      return "upstream_error";
  }
}
__name(mapRfc8628OAuthCode, "mapRfc8628OAuthCode");

// packages/cli/src/serve/server/device-flow-registry.ts
function setupDeviceFlowRegistry(deps) {
  const deviceFlowProviderMap = /* @__PURE__ */ new Map();
  for (const provider of deps.providers ?? []) {
    deviceFlowProviderMap.set(provider.providerId, provider);
  }
  if (!deviceFlowProviderMap.has("qwen-oauth")) {
    deviceFlowProviderMap.set("qwen-oauth", new QwenOAuthDeviceFlowProvider());
  }
  const deviceFlowEventSink = {
    publish(emission, originatorClientId) {
      deps.bridge.publishWorkspaceEvent({
        type: `auth_device_flow_${emission.type}`,
        data: emission.data,
        ...originatorClientId ? { originatorClientId } : {}
      });
    }
  };
  const deviceFlowRegistry = deps.registry ?? new DeviceFlowRegistry({
    events: deviceFlowEventSink,
    audit: {
      record(line) {
        const id = line.deviceFlowId.slice(0, 8);
        const parts = [
          `[serve] auth.device-flow:`,
          `provider=${line.providerId}`,
          `deviceFlowId=${id}...`,
          line.clientId ? `clientId=${line.clientId}` : "clientId=-",
          `status=${line.status}`
        ];
        if (line.errorKind) parts.push(`errorKind=${line.errorKind}`);
        if (line.expiresInMs !== void 0) {
          parts.push(`expiresInMs=${Math.max(0, line.expiresInMs)}`);
        }
        if (line.hint) {
          const STDERR_HINT_MAX = 1024;
          const hint = line.hint.length > STDERR_HINT_MAX ? `${line.hint.slice(0, STDERR_HINT_MAX)}\u2026[+${line.hint.length - STDERR_HINT_MAX} bytes truncated]` : line.hint;
          parts.push(`hint=${JSON.stringify(hint)}`);
        }
        writeStderrLine(parts.join(" "));
      }
    },
    resolveProvider: /* @__PURE__ */ __name((providerId) => deviceFlowProviderMap.get(providerId), "resolveProvider")
  });
  setDeviceFlowRegistry(deps.app, deviceFlowRegistry);
  return {
    deviceFlowRegistry,
    getSupportedDeviceFlowProviders: /* @__PURE__ */ __name(() => Array.from(deviceFlowProviderMap.keys()), "getSupportedDeviceFlowProviders")
  };
}
__name(setupDeviceFlowRegistry, "setupDeviceFlowRegistry");

// packages/cli/src/serve/server/error-handlers.ts
init_esbuild_shims();
var import_express3 = __toESM(require_express(), 1);
function installJsonBodyParser(app) {
  app.use(import_express3.default.json({ limit: "10mb" }));
  app.use((err, _req, res, next) => {
    if (sendJsonBodyParserError(res, err)) return;
    next(err);
  });
}
__name(installJsonBodyParser, "installJsonBodyParser");
function installFinalErrorHandler(app) {
  app.use((err, _req, res, _next) => {
    if (sendJsonBodyParserError(res, err)) return;
    writeStderrLine(
      `qwen serve: unhandled error: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
    );
    if (!res.headersSent) {
      res.status(500).json({ error: "Internal server error" });
    }
  });
}
__name(installFinalErrorHandler, "installFinalErrorHandler");

// packages/cli/src/serve/server/rate-limiter-setup.ts
init_esbuild_shims();
function installRateLimiter(app, opts, daemonLog) {
  if (!opts.rateLimit) return void 0;
  const windowMs = opts.rateLimitWindowMs ?? 6e4;
  const rateLimiter = createRateLimiter({
    tiers: {
      prompt: { windowMs, max: opts.rateLimitPrompt ?? 10 },
      mutation: { windowMs, max: opts.rateLimitMutation ?? 30 },
      read: { windowMs, max: opts.rateLimitRead ?? 120 }
    },
    hostname: opts.hostname,
    onLimitReached: daemonLog ? (tier, key, suppressed) => {
      daemonLog.warn(
        `rate limit hit${suppressed > 0 ? ` (${suppressed} suppressed)` : ""}`,
        { tier, key: key.slice(0, 64) }
      );
    } : void 0,
    onError: daemonLog ? (err, path13) => {
      daemonLog.warn(
        `rate limiter error (fail-open): ${err instanceof Error ? err.message : String(err)}`,
        { path: path13 }
      );
    } : void 0
  });
  app.use(rateLimiter.middleware);
  return rateLimiter;
}
__name(installRateLimiter, "installRateLimiter");

// packages/cli/src/serve/server/self-origin.ts
init_esbuild_shims();
function installSelfOriginStripMiddleware(app, getPort) {
  let cachedStripPort = -1;
  let cachedSelfOrigins = /* @__PURE__ */ new Set();
  app.use((req, _res, next) => {
    const origin = req.headers.origin;
    if (origin) {
      const port = getPort();
      if (port !== cachedStripPort) {
        cachedStripPort = port;
        cachedSelfOrigins = /* @__PURE__ */ new Set([
          `http://127.0.0.1:${port}`,
          `http://localhost:${port}`,
          `http://[::1]:${port}`,
          `http://host.docker.internal:${port}`
        ]);
      }
      if (cachedSelfOrigins.has(origin)) {
        delete req.headers.origin;
      }
    }
    next();
  });
}
__name(installSelfOriginStripMiddleware, "installSelfOriginStripMiddleware");

// packages/cli/src/serve/routes/workspace-lifecycle.ts
init_esbuild_shims();
function registerWorkspaceLifecycleRoutes(app, deps) {
  const {
    boundWorkspace,
    workspace,
    mutate,
    safeBody: safeBody2,
    sendBridgeError: sendBridgeError2,
    invalidateServeFeaturesCache,
    parseAndValidateClientId
  } = deps;
  const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
  app.post("/workspace/init", mutate({ strict: true }), async (req, res) => {
    const body = safeBody2(req);
    const force = body["force"];
    if (force !== void 0 && typeof force !== "boolean") {
      res.status(400).json({
        error: "`force` must be a boolean when provided",
        code: "invalid_force_flag"
      });
      return;
    }
    const clientId = parseAndValidateClientId(req, res);
    if (clientId === null) return;
    try {
      const ctx = buildWorkspaceCtx("POST /workspace/init", clientId);
      const result = await workspace.initWorkspace(ctx, {
        force: force === true
      });
      res.status(200).json(result);
    } catch (err) {
      sendBridgeError2(res, err, { route: "POST /workspace/init" });
    }
  });
  app.post("/workspace/reload", mutate({ strict: true }), async (req, res) => {
    const clientId = parseAndValidateClientId(req, res);
    if (clientId === null) return;
    try {
      const ctx = buildWorkspaceCtx("POST /workspace/reload", clientId);
      const result = await workspace.reload(ctx);
      invalidateServeFeaturesCache();
      res.status(200).json(result);
    } catch (err) {
      sendBridgeError2(res, err, { route: "POST /workspace/reload" });
    }
  });
}
__name(registerWorkspaceLifecycleRoutes, "registerWorkspaceLifecycleRoutes");

// packages/cli/src/serve/routes/workspace-mcp-control.ts
init_esbuild_shims();
function registerWorkspaceMcpControlRoutes(app, deps) {
  const {
    boundWorkspace,
    bridge,
    workspace,
    mutate,
    safeBody: safeBody2,
    sendBridgeError: sendBridgeError2,
    parseAndValidateClientId
  } = deps;
  const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
  app.post(
    "/workspace/mcp/:server/restart",
    mutate({ strict: true }),
    async (req, res) => {
      const serverName = req.params["server"];
      if (!serverName || typeof serverName !== "string") {
        res.status(400).json({
          error: "Server name path parameter is required",
          code: "invalid_server_name"
        });
        return;
      }
      if (serverName.length > MAX_SERVER_NAME_LENGTH) {
        res.status(400).json({
          error: `Server name exceeds ${MAX_SERVER_NAME_LENGTH}-character limit`,
          code: "invalid_server_name"
        });
        return;
      }
      const clientId = parseAndValidateClientId(req, res);
      if (clientId === null) return;
      let entryIndex;
      const rawEntryIndex = req.query["entryIndex"];
      if (rawEntryIndex !== void 0 && rawEntryIndex !== "*") {
        const candidate = typeof rawEntryIndex === "string" ? rawEntryIndex : void 0;
        const parsed = candidate !== void 0 ? Number.parseInt(candidate, 10) : NaN;
        if (!Number.isInteger(parsed) || parsed < 0 || String(parsed) !== candidate) {
          res.status(400).json({
            error: '`entryIndex` query parameter must be a non-negative integer or "*"',
            code: "invalid_entry_index"
          });
          return;
        }
        entryIndex = parsed;
      }
      try {
        const ctx = buildWorkspaceCtx(
          "POST /workspace/mcp/:server/restart",
          clientId
        );
        const result = await workspace.restartMcpServer(
          ctx,
          serverName,
          entryIndex !== void 0 ? { entryIndex } : void 0
        );
        res.status(200).json(result);
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/mcp/:server/restart"
        });
      }
    }
  );
  for (const [routeAction, bridgeAction] of [
    ["enable", "enable"],
    ["disable", "disable"],
    ["authenticate", "authenticate"],
    ["clear-auth", "clear-auth"]
  ]) {
    app.post(
      `/workspace/mcp/:server/${routeAction}`,
      mutate({ strict: true }),
      async (req, res) => {
        const serverName = req.params["server"];
        if (!serverName || typeof serverName !== "string") {
          res.status(400).json({
            error: "Server name path parameter is required",
            code: "invalid_server_name"
          });
          return;
        }
        if (serverName.length > MAX_SERVER_NAME_LENGTH) {
          res.status(400).json({
            error: `Server name exceeds ${MAX_SERVER_NAME_LENGTH}-character limit`,
            code: "invalid_server_name"
          });
          return;
        }
        const clientId = parseAndValidateClientId(req, res);
        if (clientId === null) return;
        try {
          const result = await bridge.manageMcpServer(
            serverName,
            bridgeAction,
            clientId
          );
          res.status(200).json(result);
        } catch (err) {
          sendBridgeError2(res, err, {
            route: `POST /workspace/mcp/:server/${routeAction}`
          });
        }
      }
    );
  }
  app.post(
    "/workspace/mcp/servers",
    mutate({ strict: true }),
    async (req, res) => {
      const body = safeBody2(req);
      const name = body["name"];
      if (!validateMcpRuntimeServerName(name, res)) return;
      const config = body["config"];
      if (typeof config !== "object" || config === null || Array.isArray(config)) {
        res.status(400).json({
          error: "`config` must be a non-null object",
          code: "missing_required_field",
          field: "config"
        });
        return;
      }
      const clientId = parseAndValidateClientId(req, res);
      if (clientId === null) return;
      if (!clientId) {
        res.status(400).json({
          error: "`X-Qwen-Client-Id` header is required for runtime MCP mutation",
          code: "missing_client_id"
        });
        return;
      }
      try {
        const result = await bridge.addRuntimeMcpServer(
          name,
          config,
          clientId
        );
        res.status(200).json(result);
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/mcp/servers"
        });
      }
    }
  );
  app.delete(
    "/workspace/mcp/servers/:name",
    mutate({ strict: true }),
    async (req, res) => {
      const name = req.params["name"] ?? "";
      if (!validateMcpRuntimeServerName(name, res)) return;
      const clientId = parseAndValidateClientId(req, res);
      if (clientId === null) return;
      if (!clientId) {
        res.status(400).json({
          error: "`X-Qwen-Client-Id` header is required for runtime MCP mutation",
          code: "missing_client_id"
        });
        return;
      }
      try {
        const result = await bridge.removeRuntimeMcpServer(name, clientId);
        res.status(200).json(result);
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "DELETE /workspace/mcp/servers/:name"
        });
      }
    }
  );
}
__name(registerWorkspaceMcpControlRoutes, "registerWorkspaceMcpControlRoutes");

// packages/cli/src/serve/routes/workspace-tools.ts
init_esbuild_shims();
function registerWorkspaceToolsRoutes(app, deps) {
  const {
    boundWorkspace,
    workspace,
    mutate,
    safeBody: safeBody2,
    sendBridgeError: sendBridgeError2,
    parseAndValidateClientId
  } = deps;
  const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
  app.post(
    "/workspace/tools/:name/enable",
    mutate({ strict: true }),
    async (req, res) => {
      const rawToolName = req.params["name"];
      if (!rawToolName || typeof rawToolName !== "string") {
        res.status(400).json({
          error: "Tool name path parameter is required",
          code: "invalid_tool_name"
        });
        return;
      }
      const toolName = rawToolName.trim();
      if (toolName.length === 0) {
        res.status(400).json({
          error: "Tool name path parameter is required",
          code: "invalid_tool_name"
        });
        return;
      }
      if (toolName.length > MAX_TOOL_NAME_LENGTH) {
        res.status(400).json({
          error: `Tool name exceeds ${MAX_TOOL_NAME_LENGTH}-character limit`,
          code: "invalid_tool_name"
        });
        return;
      }
      const body = safeBody2(req);
      const enabled = body["enabled"];
      if (typeof enabled !== "boolean") {
        res.status(400).json({
          error: "`enabled` is required and must be a boolean",
          code: "invalid_enabled_flag"
        });
        return;
      }
      const clientId = parseAndValidateClientId(req, res);
      if (clientId === null) return;
      try {
        const ctx = buildWorkspaceCtx(
          "POST /workspace/tools/:name/enable",
          clientId
        );
        const result = await workspace.setWorkspaceToolEnabled(
          ctx,
          toolName,
          enabled
        );
        res.status(200).json(result);
      } catch (err) {
        sendBridgeError2(res, err, {
          route: "POST /workspace/tools/:name/enable"
        });
      }
    }
  );
}
__name(registerWorkspaceToolsRoutes, "registerWorkspaceToolsRoutes");

// packages/cli/src/serve/server.ts
var warnedDefaultTrust = false;
function createServeApp(opts, getPort = () => opts.port, deps = {}) {
  const app = (0, import_express4.default)();
  const boundWorkspace = deps.boundWorkspace ?? canonicalizeWorkspace(opts.workspace ?? process.cwd());
  if (!deps.fsFactory && !deps.bridge && !warnedDefaultTrust) {
    warnedDefaultTrust = true;
    process.stderr.write(
      "qwen serve: createServeApp default fsFactory uses trusted=false \u2014 agent ACP writeTextFile calls will reject with untrusted_workspace. Inject deps.fsFactory (with explicit trust) or deps.bridge to override.\n"
    );
  }
  const fsFactory = resolveBridgeFsFactory({
    boundWorkspace,
    injected: deps.fsFactory,
    trusted: false
  });
  const tokenConfigured = typeof opts.token === "string" && opts.token.length > 0;
  const sessionShellCommandEnabled = opts.enableSessionShell === true && tokenConfigured;
  if (opts.clientMcpOverWs === true && deps.bridge && !deps.clientMcpSenderRegistry) {
    throw new Error(
      "createServeApp: deps.bridge requires deps.clientMcpSenderRegistry when clientMcpOverWs is enabled (the bridge is already wired to its own sender; a fresh registry here would be an orphan)."
    );
  }
  const clientMcpSenderRegistry = deps.clientMcpSenderRegistry ?? new ClientMcpSenderRegistry();
  const { languageCodes, currentServeFeatures, invalidateServeFeaturesCache } = createServeFeatures({
    opts,
    boundWorkspace,
    persistSettingAvailable: deps.persistSetting !== void 0,
    reloadAvailable: deps.workspace !== void 0,
    sessionShellCommandEnabled
  });
  const statusProvider = deps.statusProvider ?? createDaemonStatusProvider();
  const bridge = deps.bridge ?? createAcpSessionBridge({
    maxSessions: opts.maxSessions,
    maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession,
    eventRingSize: opts.eventRingSize,
    permissionResponseTimeoutMs: opts.permissionResponseTimeoutMs,
    boundWorkspace,
    sessionShellCommandEnabled,
    // Wire the production status provider so direct embeds / tests
    // that don't inject `deps.bridge` get daemon env + preflight cells.
    statusProvider,
    // Wire the WorkspaceFileSystem adapter so ACP writeTextFile /
    // readTextFile pick up trust / TOCTOU / audit.
    fileSystem: createBridgeFileSystemAdapter(fsFactory),
    // Reverse tool channel: answer the child's `client_mcp/message`
    // ext-method by reaching the WS connection that hosts the named server.
    clientMcpSender: clientMcpSenderRegistry.lookup
  });
  const archiveCoordinator = new SessionArchiveCoordinator();
  installSelfOriginStripMiddleware(app, getPort);
  app.locals.fsFactory = fsFactory;
  app.locals.boundWorkspace = boundWorkspace;
  const { deviceFlowRegistry, getSupportedDeviceFlowProviders } = setupDeviceFlowRegistry({
    app,
    bridge,
    registry: deps.deviceFlowRegistry,
    providers: deps.deviceFlowProviders
  });
  const { daemonLog } = deps;
  const sendBridgeError2 = /* @__PURE__ */ __name((res, err, ctx) => sendBridgeError(res, err, ctx, daemonLog), "sendBridgeError");
  const sendPermissionVoteError2 = /* @__PURE__ */ __name((res, err, ctx) => sendPermissionVoteError(res, err, ctx, daemonLog), "sendPermissionVoteError");
  const workspace = deps.workspace ?? createDaemonWorkspaceService({
    boundWorkspace,
    contextFilename: deps.contextFilename ?? "QWEN.md",
    statusProvider,
    workspaceProvidersStatusProvider: createWorkspaceProvidersStatusProvider(),
    isChannelLive: /* @__PURE__ */ __name(() => bridge.isChannelLive(), "isChannelLive"),
    persistDisabledTools: deps.persistDisabledTools ?? (async () => {
      throw new Error(
        "setWorkspaceToolEnabled requires persistDisabledTools in ServeAppDeps"
      );
    }),
    queryWorkspaceStatus: /* @__PURE__ */ __name((method, idle) => bridge.queryWorkspaceStatus(method, idle), "queryWorkspaceStatus"),
    invokeWorkspaceCommand: /* @__PURE__ */ __name((method, params, invokeOpts) => bridge.invokeWorkspaceCommand(method, params, invokeOpts), "invokeWorkspaceCommand"),
    refreshExtensionsForAllSessions: /* @__PURE__ */ __name(() => bridge.refreshExtensionsForAllSessions(), "refreshExtensionsForAllSessions"),
    ...deps.persistSetting ? { persistSetting: deps.persistSetting } : {},
    ...deps.persistSettings ? { persistSettings: deps.persistSettings } : {},
    publishWorkspaceEvent: /* @__PURE__ */ __name((event) => {
      if (event.type === "settings_changed" || event.type === "settings_reloaded") {
        invalidateServeFeaturesCache();
      }
      bridge.publishWorkspaceEvent(event);
    }, "publishWorkspaceEvent")
  });
  if (opts.allowOrigins && opts.allowOrigins.length > 0) {
    const parsedAllowOrigins = parseAllowOriginPatterns(opts.allowOrigins);
    if (parsedAllowOrigins.allowAny && !opts.token) {
      throw new Error(
        `Refusing to start with --allow-origin '*' but no bearer token configured. '*' admits any cross-origin browser to the API; without a token, any local page can drive the daemon. Set a token or list specific origins instead of '*'.`
      );
    }
    app.use(allowOriginCors(parsedAllowOrigins));
  } else {
    app.use(denyBrowserOriginCors);
  }
  app.use(hostAllowlist(opts.hostname, getPort));
  const healthDemoRoutes = createHealthDemoRoutes({
    opts,
    getPort,
    bridge,
    getActiveSseCount,
    getRateLimiter: /* @__PURE__ */ __name(() => rateLimiter, "getRateLimiter")
  });
  if (healthDemoRoutes.exposeHealthPreAuth) {
    healthDemoRoutes.register(app);
  }
  installAccessLogMiddleware(app, daemonLog);
  const webShellDir = opts.serveWebShell !== false ? deps.webShellDir : void 0;
  const webShellFrameAncestors = opts.allowOrigins && opts.allowOrigins.length > 0 ? [...parseAllowOriginPatterns(opts.allowOrigins).origins].filter(
    (o) => o.startsWith("chrome-extension://") || o.startsWith("moz-extension://")
  ) : [];
  if (webShellDir) {
    mountWebShellAssets(app, webShellDir, webShellFrameAncestors);
  }
  app.use(bearerAuth(opts.token));
  const rateLimiter = installRateLimiter(app, opts, daemonLog);
  installJsonBodyParser(app);
  if (!healthDemoRoutes.exposeHealthPreAuth) {
    healthDemoRoutes.register(app);
  }
  const mutate = createMutationGate({
    tokenConfigured,
    requireAuth: opts.requireAuth === true
  });
  app.use(daemonTelemetryMiddleware(boundWorkspace));
  const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
  const acpHandleRef = {};
  const workspaceRememberLane = new WorkspaceRememberTaskLane(bridge);
  const cdpTunnelRegistry = opts.cdpTunnelOverWs === true ? new CdpTunnelRegistry() : void 0;
  registerDaemonStatusRoutes(app, {
    opts,
    boundWorkspace,
    bridge,
    workspace,
    daemonLog,
    startup: deps.startup,
    qwenCodeVersion: deps.qwenCodeVersion,
    getAcpHandle: /* @__PURE__ */ __name(() => acpHandleRef.current, "getAcpHandle"),
    getRateLimiter: /* @__PURE__ */ __name(() => rateLimiter, "getRateLimiter"),
    getRestSseActive: getActiveSseCount,
    currentServeFeatures,
    getSupportedDeviceFlowProviders,
    deviceFlowRegistry,
    sessionShellCommandEnabled,
    getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot
  });
  registerCapabilitiesRoutes(app, {
    qwenCodeVersion: deps.qwenCodeVersion,
    mode: opts.mode,
    currentServeFeatures,
    boundWorkspace,
    permissionPolicy: bridge.permissionPolicy,
    maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession,
    languageCodes
  });
  registerWorkspaceStatusRoutes(app, {
    boundWorkspace,
    bridge,
    workspace,
    sendBridgeError: sendBridgeError2
  });
  mountWorkspaceMemoryRoutes(app, {
    bridge,
    boundWorkspace,
    mutate,
    parseClientId: parseClientIdHeader,
    safeBody
  });
  mountWorkspaceMemoryRememberRoutes(app, {
    bridge,
    lane: workspaceRememberLane,
    mutate,
    parseClientId: parseClientIdHeader,
    safeBody
  });
  mountWorkspaceAgentsRoutes(app, {
    bridge,
    boundWorkspace,
    mutate,
    parseClientId: parseClientIdHeader,
    safeBody
  });
  registerWorkspaceDiagnosticStatusRoutes(app, {
    boundWorkspace,
    bridge,
    workspace,
    sendBridgeError: sendBridgeError2
  });
  registerWorkspaceExtensionRoutes(app, {
    boundWorkspace,
    bridge,
    workspace,
    mutate,
    safeBody,
    sendBridgeError: sendBridgeError2,
    ...deps.maxExtensionOperationHistory === void 0 ? {} : { maxExtensionOperationHistory: deps.maxExtensionOperationHistory }
  });
  registerWorkspaceFileReadRoutes(app, {
    parseClientId: parseClientIdHeader
  });
  registerWorkspaceFileWriteRoutes(app, {
    bridge,
    mutate,
    parseClientId: parseClientIdHeader,
    safeBody
  });
  registerWorkspaceSetupGithubRoutes(app, {
    boundWorkspace,
    bridge,
    mutate,
    parseClientId: parseClientIdHeader,
    safeBody
  });
  registerWorkspaceTrustRoutes(app, {
    boundWorkspace,
    workspace,
    mutate,
    safeBody,
    parseAndValidateClientId: /* @__PURE__ */ __name((req, res) => parseAndValidateWorkspaceClientId(req, res, bridge), "parseAndValidateClientId")
  });
  const broadcastSettingsChanged = /* @__PURE__ */ __name((key, value, scope, clientId) => {
    invalidateServeFeaturesCache();
    bridge.publishWorkspaceEvent({
      type: "settings_changed",
      data: { key, value, scope },
      ...clientId ? { originatorClientId: clientId } : {}
    });
  }, "broadcastSettingsChanged");
  if (deps.persistSetting) {
    const persistSetting = deps.persistSetting;
    registerWorkspaceSettingsRoutes(app, {
      boundWorkspace,
      mutate,
      safeBody,
      persistSetting: /* @__PURE__ */ __name(async (...args) => {
        await persistSetting(...args);
      }, "persistSetting"),
      broadcastSettingsChanged,
      parseAndValidateClientId: /* @__PURE__ */ __name((req, res) => parseAndValidateWorkspaceClientId(req, res, bridge), "parseAndValidateClientId")
    });
  }
  registerWorkspacePermissionsRoutes(app, {
    boundWorkspace,
    mutate,
    safeBody,
    workspace,
    parseAndValidateClientId: /* @__PURE__ */ __name((req, res) => parseAndValidateWorkspaceClientId(req, res, bridge), "parseAndValidateClientId")
  });
  registerWorkspaceVoiceRoutes(app, {
    boundWorkspace,
    mutate,
    safeBody,
    persistSetting: deps.persistSetting,
    persistSettings: deps.persistSettings,
    transcribe: deps.voiceTranscriber,
    broadcastSettingsChanged,
    parseAndValidateClientId: /* @__PURE__ */ __name((req, res) => parseAndValidateWorkspaceClientId(req, res, bridge), "parseAndValidateClientId")
  });
  registerA2uiActionRoutes(app, {
    boundWorkspace,
    mutate,
    safeBody,
    // UI-server discovery uses the daemon's workspace MCP status, which
    // includes servers registered at runtime.
    getMcpServers: /* @__PURE__ */ __name(async () => {
      const ctx = buildWorkspaceCtx("POST /session/:id/a2ui-action");
      const status = await workspace.getWorkspaceMcpStatus(ctx);
      return status.servers ?? [];
    }, "getMcpServers")
  });
  registerWorkspaceAuthRoutes(app, {
    mutate,
    deviceFlowRegistry,
    getSupportedDeviceFlowProviders,
    sendBridgeError: sendBridgeError2,
    boundWorkspace,
    allowPrivateAuthBaseUrl: opts.allowPrivateAuthBaseUrl === true,
    installAuthProvider: deps.installAuthProvider
  });
  registerSessionRoutes(app, {
    boundWorkspace,
    bridge,
    archiveCoordinator,
    mutate,
    sendBridgeError: sendBridgeError2,
    daemonLog,
    promptDeadlineMs: opts.promptDeadlineMs,
    sessionShellCommandEnabled,
    languageCodes
  });
  registerWorkspaceMcpControlRoutes(app, {
    boundWorkspace,
    bridge,
    workspace,
    mutate,
    safeBody,
    sendBridgeError: sendBridgeError2,
    parseAndValidateClientId: /* @__PURE__ */ __name((req, res) => parseAndValidateWorkspaceClientId(req, res, bridge), "parseAndValidateClientId")
  });
  registerWorkspaceLifecycleRoutes(app, {
    boundWorkspace,
    workspace,
    mutate,
    safeBody,
    sendBridgeError: sendBridgeError2,
    invalidateServeFeaturesCache,
    parseAndValidateClientId: /* @__PURE__ */ __name((req, res) => parseAndValidateWorkspaceClientId(req, res, bridge), "parseAndValidateClientId")
  });
  registerWorkspaceToolsRoutes(app, {
    boundWorkspace,
    workspace,
    mutate,
    safeBody,
    sendBridgeError: sendBridgeError2,
    parseAndValidateClientId: /* @__PURE__ */ __name((req, res) => parseAndValidateWorkspaceClientId(req, res, bridge), "parseAndValidateClientId")
  });
  registerPermissionRoutes(app, {
    bridge,
    mutate,
    sendPermissionVoteError: sendPermissionVoteError2
  });
  registerSseEventsRoutes(app, {
    bridge,
    daemonLog,
    writerIdleTimeoutMs: opts.writerIdleTimeoutMs,
    sendBridgeError: sendBridgeError2
  });
  acpHandleRef.current = mountAcpHttp(app, bridge, {
    boundWorkspace,
    archiveCoordinator,
    workspace,
    fsFactory,
    deviceFlowRegistry,
    token: opts.token,
    // Mirror the REST CORS allowlist onto the WS CSRF wall so an
    // explicitly permitted origin (e.g. the extension's
    // `chrome-extension://<id>`) can open the reverse tool channel.
    allowedOrigins: opts.allowOrigins && opts.allowOrigins.length > 0 ? parseAllowOriginPatterns(opts.allowOrigins) : void 0,
    hostname: opts.hostname,
    sessionShellCommandEnabled,
    workspaceRememberLane,
    checkRate: rateLimiter?.checkRate,
    clientMcpOverWs: opts.clientMcpOverWs === true,
    // Reverse tool channel (issue #5626, Phase 2). Per-connection provider:
    // on `mcp_register` it records the WS registrar's sender in the shared
    // registry and adds an SDK-type runtime MCP server in the ACP child
    // (originator = the connection id). Only meaningful when
    // `clientMcpOverWs` is on; the WS layer never builds a provider otherwise.
    ...opts.clientMcpOverWs === true ? {
      clientMcpProviderFactory: /* @__PURE__ */ __name((connectionId) => createClientMcpServerProvider(
        clientMcpSenderRegistry,
        bridge,
        connectionId
      ), "clientMcpProviderFactory")
    } : {},
    // Plan C CDP tunnel (issue #5626): the `/cdp` branch + `cdp_*` routing
    // activate only when the flag is on and a registry is supplied.
    cdpTunnelOverWs: opts.cdpTunnelOverWs === true,
    ...cdpTunnelRegistry ? { cdpTunnelRegistry } : {},
    // Browser captures audio and streams raw PCM here; the daemon transcribes
    // server-side via the reused CLI voice pipeline. Shares the ACP upgrade
    // listener's loopback/CSRF/bearer checks.
    extraWsRoutes: [
      {
        path: "/voice/stream",
        onConnection: createVoiceWsConnectionHandler(boundWorkspace)
      }
    ]
  });
  if (acpHandleRef.current) {
    app.locals["acpHandle"] = acpHandleRef.current;
  }
  if (webShellDir) {
    mountWebShellSpaFallback(app, webShellDir, webShellFrameAncestors);
  }
  installFinalErrorHandler(app);
  if (rateLimiter) {
    setRateLimiter(app, rateLimiter);
  }
  return app;
}
__name(createServeApp, "createServeApp");
export {
  InvalidCursorError,
  PromptDeadlineExceededError,
  createDefaultFsAuditEmit,
  createServeApp,
  detectFromLoopback,
  getActiveSseCount,
  listWorkspaceSessionsForResponse,
  resolveBridgeFsFactory,
  resolvePromptDeadlineMs
};
/**
 * @license
 * Copyright 2025 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Reverse link for the Plan C "CDP tunnel" (issue #5626).
 *
 * Bridges a {@link CdpBrowserEmulator} (browser-level CDP to a puppeteer client
 * over `/cdp`) to the Chrome extension's reverse `/acp` WebSocket. Page-domain
 * commands the emulator can't answer are forwarded to the real tab as
 * `cdp_command` frames; the extension runs them and replies with `cdp_result`,
 * and tab `cdp_event`s are re-tagged onto the page session by the emulator.
 *
 * One link is bound to ONE extension `/acp` connection (single daemon = single
 * extension = single browser). The `/acp` WS layer owns the socket and feeds
 * inbound `cdp_*` frames back into the link.
 *
 * See `packages/chrome-extension/docs/06-plan-c-cdp-tunnel.md`.
 */
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * CDP browser-level emulation layer for the Plan C "CDP tunnel" (issue #5626).
 *
 * A puppeteer client (chrome-devtools-mcp) connects to the daemon's `/cdp`
 * WebSocket expecting a browser-level CDP endpoint, but behind the tunnel is a
 * single real tab driven via `chrome.debugger` (page-level only). This class
 * synthesizes the missing browser-level topology so puppeteer connects and gets
 * one page:
 *
 *   - a two-level `tab` -> `page` target tree, and
 *   - the recursive `Target.setAutoAttach` handshake puppeteer's
 *     `ExtensionTransport` relies on.
 *
 * Browser-domain commands are answered locally; page-domain commands (tagged
 * with the page session id) are forwarded to the real tab via
 * {@link CdpEmulatorCallbacks.forwardToTab}, and tab events are re-tagged with
 * the page session id on the way back via {@link CdpBrowserEmulator.emitTabEvent}.
 *
 * See `packages/chrome-extension/docs/06-plan-c-cdp-tunnel.md`.
 */
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * `/cdp` endpoint glue for the Plan C "CDP tunnel" (issue #5626).
 *
 * Per puppeteer connection (chrome-devtools-mcp) this wires:
 *
 *   puppeteer  --raw CDP-->  CdpBrowserEmulator  --forwardToTab-->  CdpReverseLink
 *                                                                        |
 *                                            extension `/acp` socket  <--+
 *
 * The emulator answers browser-level CDP locally and forwards page-domain
 * commands to the real tab over the reverse link; tab events flow back. One
 * `/cdp` connection binds to the (single) active extension bridge in the
 * {@link CdpTunnelRegistry}; if no extension is connected the socket is closed
 * immediately with a clear reason.
 *
 * See `packages/chrome-extension/docs/06-plan-c-cdp-tunnel.md`.
 */
/**
 * @license
 * Copyright 2025 Qwen
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Process-scoped registry pairing the (single) extension `/acp` reverse
 * connection with the `/cdp` endpoint for the Plan C "CDP tunnel" (issue #5626).
 *
 * The `/acp` WS layer registers an outbound `cdp_*` sink here when an extension
 * becomes the active CDP bridge; the `/cdp` endpoint looks it up to build a
 * {@link CdpReverseLink}. Single daemon = single extension = single browser, so
 * at most one bridge is held (last-writer-wins; a reconnect supersedes a stale
 * one). Mirrors `ClientMcpSenderRegistry` so both reverse channels wire the same
 * way through `server.ts`.
 */
