// Force strict mode and setup for ESM
"use strict";
import {
  MID_TURN_QUEUE_DRAIN_METHOD
} from "./chunk-QBZ22PQX.js";
import {
  MCP_RESTART_SERVER_DEADLINE_MS
} from "./chunk-P7M4GA57.js";
import {
  BranchWhilePromptActiveError,
  CancelSentinelCollisionError,
  InvalidClientIdError,
  InvalidPermissionOptionError,
  InvalidRewindTargetError,
  InvalidSessionMetadataError,
  InvalidSessionScopeError,
  PermissionForbiddenError,
  PromptQueueFullError,
  RestoreInProgressError,
  SessionBusyError,
  SessionLimitExceededError,
  SessionNotFoundError,
  SessionShellClientRequiredError,
  SessionShellDisabledError,
  WorkspaceMismatchError,
  isNotCurrentlyGeneratingCancelError
} from "./chunk-S566HKUS.js";
import {
  canonicalizeWorkspace
} from "./chunk-SEZC2725.js";
import {
  DEFAULT_RING_SIZE,
  EVENT_SCHEMA_VERSION,
  EventBus
} from "./chunk-ALX6BUYW.js";
import {
  defaultSpawnChannelFactory
} from "./chunk-3IASHX6S.js";
import {
  ClientSideConnection,
  PROTOCOL_VERSION,
  RequestError
} from "./chunk-UFC57OYT.js";
import {
  BridgeChannelClosedError,
  BridgeTimeoutError,
  SERVE_CONTROL_EXT_METHODS,
  SERVE_STATUS_EXT_METHODS,
  STATUS_SCHEMA_VERSION,
  createIdleWorkspaceExtensionsStatus,
  createIdleWorkspaceHooksStatus
} from "./chunk-6G3UPYRX.js";
import {
  ShellExecutionService,
  TrustGateError
} from "./chunk-Z3SLAUKQ.js";
import {
  DAEMON_TRACEPARENT_META_KEY,
  DAEMON_TRACESTATE_META_KEY
} from "./chunk-QSRNLMOT.js";
import {
  init_esbuild_shims
} from "./chunk-A4BMJM77.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/acp-bridge/src/bridge.ts
init_esbuild_shims();
import { randomUUID as randomUUID2 } from "node:crypto";
import * as path2 from "node:path";

// packages/acp-bridge/src/compactionEngine.ts
init_esbuild_shims();
var TURN_BOUNDARY_TYPES = /* @__PURE__ */ new Set(["turn_complete", "turn_error"]);
var TRANSIENT_TYPES = /* @__PURE__ */ new Set([
  "slow_client_warning",
  "client_evicted",
  "replay_complete",
  "stream_error"
]);
var LATEST_WINS_UPDATES = /* @__PURE__ */ new Set([
  "available_commands_update",
  "current_mode_update"
]);
var TurnBoundaryCompactionEngine = class {
  static {
    __name(this, "TurnBoundaryCompactionEngine");
  }
  compactedTurns = [];
  liveJournal = [];
  lastEventId = 0;
  closed = false;
  slots = [];
  toolSlotIndex = /* @__PURE__ */ new Map();
  textSlotIndex = /* @__PURE__ */ new Map();
  ingest(event) {
    if (this.closed) return;
    if (event.id !== void 0) {
      this.lastEventId = event.id;
    }
    if (TRANSIENT_TYPES.has(event.type)) return;
    this.liveJournal.push(event);
    if (TURN_BOUNDARY_TYPES.has(event.type)) {
      this.compactCurrentTurn(event);
      return;
    }
    if (event.type === "session_update") {
      this.classifySessionUpdate(event);
      return;
    }
    this.slots.push({ kind: "misc", event });
  }
  snapshot() {
    return {
      compactedTurns: this.compactedTurns.slice(),
      liveJournal: this.liveJournal.slice(),
      lastEventId: this.lastEventId
    };
  }
  seed(snapshot) {
    if (this.closed) return;
    this.compactedTurns = snapshot.compactedTurns.slice();
    this.lastEventId = snapshot.lastEventId;
    this.liveJournal = [];
    this.slots = [];
    this.toolSlotIndex.clear();
    this.textSlotIndex.clear();
  }
  close() {
    if (this.closed) return;
    this.closed = true;
    this.compactedTurns = [];
    this.liveJournal = [];
    this.slots = [];
    this.toolSlotIndex.clear();
    this.textSlotIndex.clear();
  }
  classifySessionUpdate(event) {
    const data = event.data;
    const updateType = data?.update?.sessionUpdate;
    if (!updateType) {
      this.slots.push({ kind: "misc", event });
      return;
    }
    switch (updateType) {
      case "agent_message_chunk": {
        this.mergeTextSlot("text", event, data);
        break;
      }
      case "agent_thought_chunk": {
        this.mergeTextSlot("thought", event, data);
        break;
      }
      case "tool_call":
      case "tool_call_update": {
        const toolCallId = data?.update?.toolCallId;
        if (!toolCallId) {
          this.slots.push({ kind: "misc", event });
          break;
        }
        const existingIdx = this.toolSlotIndex.get(toolCallId);
        if (existingIdx !== void 0) {
          const slot = this.slots[existingIdx];
          slot.event = mergeToolCallEvent(slot.event, event);
        } else {
          const normalizedEvent = normalizeToolCallType(event);
          this.toolSlotIndex.set(toolCallId, this.slots.length);
          this.slots.push({
            kind: "tool",
            toolCallId,
            event: normalizedEvent
          });
          const toolParent = extractParentToolCallIdFromMeta(
            data?.update?._meta
          );
          if (toolParent) {
            this.textSlotIndex.delete(`text::${toolParent}`);
            this.textSlotIndex.delete(`thought::${toolParent}`);
          }
        }
        break;
      }
      default: {
        if (LATEST_WINS_UPDATES.has(updateType)) {
          const existingIdx = this.slots.findIndex(
            (s) => s.kind === "latestWins" && s.key === updateType
          );
          if (existingIdx !== -1) {
            this.slots[existingIdx].event = event;
          } else {
            this.slots.push({ kind: "latestWins", key: updateType, event });
          }
        } else {
          this.slots.push({ kind: "misc", event });
        }
        break;
      }
    }
  }
  mergeTextSlot(kind, event, data) {
    const text = data?.update?.content?.text ?? "";
    const meta = data?.update?._meta;
    const parentToolCallId = extractParentToolCallIdFromMeta(meta);
    if (parentToolCallId != null) {
      const slotKey = `${kind}::${parentToolCallId}`;
      const existingIdx = this.textSlotIndex.get(slotKey);
      if (existingIdx !== void 0) {
        const slot = this.slots[existingIdx];
        slot.chunks.push(text);
        if (event.id !== void 0) slot.lastEventId = event.id;
        slot.lastMeta = meta ?? slot.lastMeta;
        slot.lastEnvelopeMeta = event._meta ?? slot.lastEnvelopeMeta;
      } else {
        this.textSlotIndex.set(slotKey, this.slots.length);
        this.slots.push({
          kind,
          parentToolCallId,
          chunks: [text],
          lastEventId: event.id ?? 0,
          lastMeta: meta,
          lastEnvelopeMeta: event._meta
        });
      }
    } else {
      const lastSlot = this.slots[this.slots.length - 1];
      if (lastSlot && lastSlot.kind === kind && lastSlot.parentToolCallId == null) {
        lastSlot.chunks.push(text);
        if (event.id !== void 0) lastSlot.lastEventId = event.id;
        lastSlot.lastMeta = meta ?? lastSlot.lastMeta;
        lastSlot.lastEnvelopeMeta = event._meta ?? lastSlot.lastEnvelopeMeta;
      } else {
        this.slots.push({
          kind,
          parentToolCallId: void 0,
          chunks: [text],
          lastEventId: event.id ?? 0,
          lastMeta: meta,
          lastEnvelopeMeta: event._meta
        });
      }
    }
  }
  compactCurrentTurn(boundaryEvent) {
    const compacted = [];
    for (const slot of this.slots) {
      switch (slot.kind) {
        case "text":
        case "thought":
          compacted.push(
            makeMergedSessionUpdateEvent(
              slot.kind === "text" ? "agent_message_chunk" : "agent_thought_chunk",
              slot.chunks.join(""),
              slot.lastEventId,
              slot.lastMeta,
              slot.lastEnvelopeMeta
            )
          );
          break;
        case "tool":
        case "misc":
        case "latestWins":
          compacted.push(slot.event);
          break;
        default:
          break;
      }
    }
    compacted.push(boundaryEvent);
    this.compactedTurns.push(...compacted);
    this.liveJournal = [];
    this.slots = [];
    this.toolSlotIndex.clear();
    this.textSlotIndex.clear();
  }
};
function makeMergedSessionUpdateEvent(sessionUpdate, text, eventId, meta, envelopeMeta) {
  return {
    id: eventId || void 0,
    v: EVENT_SCHEMA_VERSION,
    type: "session_update",
    ...envelopeMeta !== void 0 ? { _meta: envelopeMeta } : {},
    data: {
      update: {
        sessionUpdate,
        content: { type: "text", text },
        ...meta != null ? { _meta: meta } : {}
      }
    }
  };
}
__name(makeMergedSessionUpdateEvent, "makeMergedSessionUpdateEvent");
function normalizeToolCallType(event) {
  const data = event.data;
  if (data?.update?.sessionUpdate === "tool_call_update") {
    return {
      ...event,
      data: {
        ...data,
        update: { ...data.update, sessionUpdate: "tool_call" }
      }
    };
  }
  return event;
}
__name(normalizeToolCallType, "normalizeToolCallType");
function extractParentToolCallIdFromMeta(meta) {
  if (typeof meta === "object" && meta !== null) {
    const val = meta["parentToolCallId"];
    return typeof val === "string" && val.length > 0 ? val : void 0;
  }
  return void 0;
}
__name(extractParentToolCallIdFromMeta, "extractParentToolCallIdFromMeta");
function mergeToolCallEvent(existing, incoming) {
  const existingData = existing.data;
  const incomingData = incoming.data;
  const existingUpdate = existingData?.update ?? {};
  const incomingUpdate = incomingData?.update ?? {};
  const merged = { ...existingUpdate };
  for (const [key, value] of Object.entries(incomingUpdate)) {
    if (value !== void 0 && value !== null) {
      merged[key] = value;
    }
  }
  merged["sessionUpdate"] = "tool_call";
  const mergedMeta = existing._meta || incoming._meta ? { ...existing._meta ?? {}, ...incoming._meta ?? {} } : void 0;
  return {
    id: incoming.id ?? existing.id,
    v: EVENT_SCHEMA_VERSION,
    type: "session_update",
    ...mergedMeta ? { _meta: mergedMeta } : {},
    data: {
      ...existingData,
      ...incomingData,
      update: merged
    }
  };
}
__name(mergeToolCallEvent, "mergeToolCallEvent");

// packages/acp-bridge/src/internal/stderrLine.ts
init_esbuild_shims();
function writeStderrLine(message) {
  process.stderr.write(message.endsWith("\n") ? message : `${message}
`);
}
__name(writeStderrLine, "writeStderrLine");

// packages/acp-bridge/src/bridgeClient.ts
init_esbuild_shims();
import { randomUUID } from "node:crypto";
import { promises as fs } from "node:fs";
import * as path from "node:path";

// packages/acp-bridge/src/daemonEventTypes.ts
init_esbuild_shims();
var MID_TURN_MESSAGE_INJECTED_EVENT = "mid_turn_message_injected";

// packages/acp-bridge/src/permissionMediator.ts
init_esbuild_shims();

// packages/acp-bridge/src/permission.ts
init_esbuild_shims();

// packages/acp-bridge/src/permissionMediator.ts
var CANCEL_VOTE_SENTINEL = "__cancelled__";
var MAX_RESOLVED_PERMISSION_RECORDS = 512;
function stringifyError(err) {
  try {
    if (err instanceof Error) return `${err.name}: ${err.message}`;
    return String(err);
  } catch {
    return "[unstringifiable error]";
  }
}
__name(stringifyError, "stringifyError");
function createNoOpPermissionAuditPublisher() {
  return {
    recordRequested() {
    },
    recordVoted() {
    },
    recordForbidden() {
    },
    recordResolved() {
    },
    recordTimeout() {
    }
  };
}
__name(createNoOpPermissionAuditPublisher, "createNoOpPermissionAuditPublisher");
var MultiClientPermissionMediator = class {
  static {
    __name(this, "MultiClientPermissionMediator");
  }
  policy;
  deps;
  pending = /* @__PURE__ */ new Map();
  resolved = /* @__PURE__ */ new Map();
  resolvedOrder = [];
  /**
   * Dedup flag for the
   * unanimity-required stderr breadcrumb. Without this, every
   * permission request on a 2-client consensus session would emit
   * an identical line (the unanimity condition is the NORMAL
   * operating mode for M=2, not a rare edge); a busy session with
   * many tool calls would produce dozens of duplicate stderr lines
   * within seconds. One emit per mediator (= per daemon lifetime
   * since the bridge constructs one) is enough to make the
   * configuration visible without spam.
   */
  unanimityBreadcrumbEmitted = false;
  constructor(policy, deps) {
    this.policy = policy;
    this.deps = deps;
  }
  /**
   * Register a fresh permission request from the agent.
   *
   * **Promise contract — once the Promise is returned, it never
   * rejects.** All runtime failure modes (timeout, session closure,
   * voter cancel, emit/audit publisher exceptions) are encoded as
   * `PermissionResolution { kind:'cancelled', reason:... }`.
   * Consumers can `await` the returned Promise and forward the
   * result without a `.catch()` block.
   *
   * **Synchronous-throw exception**:
   * when the agent's `allowedOptionIds` contains the
   * cancel-vote sentinel string, this method throws
   * `CancelSentinelCollisionError` synchronously BEFORE constructing
   * the Promise. The synchronous shape is intentional — a
   * never-settling Promise alongside a thrown error would be worse
   * than a clean fail-fast — but callers must wrap this method
   * itself in `try/catch` (or call it from an `async` function so
   * the throw bubbles via the function's own Promise machinery).
   * `bridgeClient.ts` currently has its own pre-check at the bridge
   * layer; embedded callers must do the same. See `@throws` below.
   *
   * **Synchronous-register invariant**: pending entry, audit
   * record, and timer setup all happen inside the Promise executor
   * without `await`. The bridge's `publish → mediator.request → await`
   * sequence relies on this — a `forgetSession` between publish and
   * await would otherwise miss the new pending and leak it until
   * timeout.
   *
   * @throws `CancelSentinelCollisionError` SYNCHRONOUSLY (not as a
   *   Promise rejection) if `record.allowedOptionIds` contains the
   *   cancel-vote sentinel string. This is a contract violation
   *   between agent and daemon and fails loudly at issue time
   *   rather than silently miscounting votes downstream. Callers
   *   inside an `async` function get the thrown error through the
   *   function's own Promise; synchronous callers must use
   *   `try/catch`.
   */
  request(record, timeoutMs) {
    if (record.allowedOptionIds.has(CANCEL_VOTE_SENTINEL)) {
      throw new CancelSentinelCollisionError(
        record.requestId,
        CANCEL_VOTE_SENTINEL
      );
    }
    return new Promise((resolve2) => {
      const policy = this.policy;
      const votersAtIssue = this.deps.votersForSession(record.sessionId);
      const pending = {
        requestId: record.requestId,
        sessionId: record.sessionId,
        policy,
        originatorClientId: record.originatorClientId,
        allowedOptionIds: record.allowedOptionIds,
        issuedAtMs: record.issuedAtMs,
        timeoutMs,
        resolve: resolve2,
        tallies: /* @__PURE__ */ new Map(),
        votersAtIssue,
        timer: void 0,
        consensusQuorumCapNoted: false
      };
      this.pending.set(record.requestId, pending);
      this.safeAudit(
        () => this.deps.audit.recordRequested(record, policy, votersAtIssue)
      );
      if (policy === "consensus" && votersAtIssue.size === 0) {
        try {
          process.stderr.write(
            `permissionMediator: consensus request ${record.requestId} for session ${record.sessionId} issued with empty votersAtIssue; can only resolve via permissionTimeoutMs (${timeoutMs}ms)
`
          );
        } catch {
        }
      }
      if (policy === "consensus" && this.deps.consensusQuorum === void 0 && votersAtIssue.size >= 2 && Math.floor(votersAtIssue.size / 2) + 1 === votersAtIssue.size && !this.unanimityBreadcrumbEmitted) {
        this.unanimityBreadcrumbEmitted = true;
        try {
          process.stderr.write(
            `permissionMediator: consensus request ${record.requestId} for session ${record.sessionId} requires unanimity (votersAtIssue.size=${votersAtIssue.size}, default quorum=floor(M/2)+1=${votersAtIssue.size}); split votes will only resolve via permissionTimeoutMs (${timeoutMs}ms). This breadcrumb fires once per mediator lifetime; subsequent unanimity-required requests are silent.
`
          );
        } catch {
        }
      }
      if (timeoutMs > 0) {
        pending.timer = setTimeout(() => {
          if (this.pending.get(record.requestId) !== pending) return;
          const firedAtMs = this.deps.now();
          try {
            process.stderr.write(
              `qwen serve: permission ${record.requestId} (session ${record.sessionId}) timed out after ${timeoutMs}ms
`
            );
          } catch {
          }
          this.safeAudit(() => this.deps.audit.recordTimeout(record));
          this.resolveEntry(
            pending,
            { kind: "cancelled", reason: "timeout" },
            {
              type: "timeout",
              issuedAtMs: pending.issuedAtMs,
              timeoutMs: pending.timeoutMs,
              firedAtMs
            },
            void 0
          );
        }, timeoutMs);
        const t = pending.timer;
        if (t && typeof t === "object" && "unref" in t) {
          t.unref();
        }
      }
    });
  }
  vote(vote) {
    const pending = this.pending.get(vote.requestId);
    if (!pending) {
      const prior = this.resolved.get(vote.requestId);
      if (prior && prior.sessionId === vote.sessionId) {
        const optionId = prior.resolution.kind === "option" ? prior.resolution.optionId : CANCEL_VOTE_SENTINEL;
        this.safeEmit(prior.sessionId, {
          type: "permission_already_resolved",
          data: {
            requestId: prior.requestId,
            sessionId: prior.sessionId,
            outcome: this.toAcpOutcome(prior.resolution)
          }
        });
        return { kind: "already_resolved", resolvedOptionId: optionId };
      }
      return { kind: "unknown_request" };
    }
    if (pending.sessionId !== vote.sessionId) {
      return { kind: "unknown_request" };
    }
    if (vote.optionId === CANCEL_VOTE_SENTINEL) {
      const outcome = {
        kind: "resolved",
        resolvedOptionId: CANCEL_VOTE_SENTINEL
      };
      this.safeAudit(
        () => this.deps.audit.recordVoted(this.toRecord(pending), vote, outcome)
      );
      this.resolveEntry(
        pending,
        { kind: "cancelled", reason: "agent_cancelled" },
        {
          type: "voter-cancelled",
          resolverClientId: vote.clientId
        },
        vote.clientId
      );
      return outcome;
    }
    if (!pending.allowedOptionIds.has(vote.optionId)) {
      throw new InvalidPermissionOptionError(vote.requestId, vote.optionId);
    }
    switch (pending.policy) {
      case "first-responder":
        return this.voteFirstResponder(pending, vote);
      case "designated":
        return this.voteDesignated(pending, vote);
      case "consensus":
        return this.voteConsensus(pending, vote);
      case "local-only":
        return this.voteLocalOnly(pending, vote);
      default: {
        const _exhaustive = pending.policy;
        void _exhaustive;
        throw new Error(`Unknown permission policy "${pending.policy}"`);
      }
    }
  }
  forgetSession(sessionId) {
    const requestIds = [];
    for (const [id, pending] of this.pending) {
      if (pending.sessionId === sessionId) requestIds.push(id);
    }
    for (const id of requestIds) {
      const pending = this.pending.get(id);
      if (!pending) continue;
      this.resolveEntry(
        pending,
        { kind: "cancelled", reason: "session_closed" },
        { type: "session-closed" },
        void 0
      );
    }
  }
  /**
   * Lookup the sessionId for a given requestId. Used by the legacy
   * `bridge.respondToPermission(requestId, ...)` route which doesn't
   * carry a sessionId in the URL. NOT part of the
   * `PermissionMediator` interface contract — bridge holds the
   * concrete class reference and calls this directly.
   */
  peekSessionFor(requestId) {
    const pending = this.pending.get(requestId);
    if (pending) return pending.sessionId;
    const prior = this.resolved.get(requestId);
    return prior?.sessionId;
  }
  /**
   * Daemon-wide in-flight pending count for diagnostics. The bridge
   * exposes this through its `pendingPermissionCount` getter so
   * operators can spot stuck FIFOs without reaching into mediator
   * internals. NOT part of the `PermissionMediator` interface
   * contract.
   */
  get pendingCount() {
    return this.pending.size;
  }
  // ===========================================================
  // Per-policy vote handlers
  // ===========================================================
  voteFirstResponder(pending, vote) {
    return this.resolveWithVote(pending, vote, {
      type: "first-responder",
      resolverClientId: vote.clientId
    });
  }
  voteDesignated(pending, vote) {
    if (pending.originatorClientId === void 0) {
      return this.voteFirstResponder(pending, vote);
    }
    if (vote.clientId !== pending.originatorClientId) {
      return this.rejectForbidden(
        pending,
        vote,
        "designated_mismatch",
        "designated_mismatch (voter is not the prompt originator)"
      );
    }
    return this.resolveWithVote(pending, vote, {
      type: "designated-originator",
      originatorClientId: pending.originatorClientId
    });
  }
  voteConsensus(pending, vote) {
    if (vote.clientId === void 0 || !pending.votersAtIssue.has(vote.clientId)) {
      return this.rejectForbidden(
        pending,
        vote,
        "designated_mismatch",
        "designated_mismatch (voter not in consensus votersAtIssue snapshot)"
      );
    }
    for (const [originalOptionId, set] of pending.tallies.entries()) {
      if (vote.clientId !== void 0 && set.has(vote.clientId)) {
        const outcome2 = {
          kind: "recorded",
          votesNeeded: this.votesNeededFor(pending)
        };
        this.safeAudit(
          () => this.deps.audit.recordVoted(
            this.toRecord(pending),
            { ...vote, optionId: originalOptionId },
            outcome2
          )
        );
        return outcome2;
      }
    }
    let bucket = pending.tallies.get(vote.optionId);
    if (!bucket) {
      bucket = /* @__PURE__ */ new Set();
      pending.tallies.set(vote.optionId, bucket);
    }
    bucket.add(vote.clientId);
    const quorum = this.consensusQuorumFor(pending);
    if (bucket.size >= quorum) {
      return this.resolveWithVote(pending, vote, {
        type: "consensus-quorum",
        resolvedOptionId: vote.optionId,
        quorum,
        tally: bucket.size
      });
    }
    const outcome = {
      kind: "recorded",
      votesNeeded: this.votesNeededFor(pending)
    };
    this.safeAudit(
      () => this.deps.audit.recordVoted(this.toRecord(pending), vote, outcome)
    );
    this.safeEmit(pending.sessionId, {
      type: "permission_partial_vote",
      data: {
        requestId: pending.requestId,
        sessionId: pending.sessionId,
        votesReceived: this.totalTalliedFor(pending),
        votesNeeded: outcome.votesNeeded,
        quorum,
        optionTallies: this.optionTalliesFor(pending)
      },
      ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
    });
    return outcome;
  }
  /**
   * Vote dispatch for `local-only` policy: only `fromLoopback: true`
   * voters can resolve a permission.
   *
   * **Cancel-sentinel asymmetry** (cancel-sentinel note).
   * `vote()` recognizes the cancel sentinel BEFORE calling this
   * method (cross-policy escape hatch — see the
   * `CANCEL_VOTE_SENTINEL` JSDoc for the rationale), so a remote
   * voter under `local-only` CAN abort a pending permission via
   * `{outcome:'cancelled'}` even though they cannot RESOLVE one. The
   * settings-side description for `local-only` and the design doc call
   * out this gap explicitly. Operators who want strict-cancel-too
   * semantics must (a) deploy a dedicated daemon process at
   * loopback bind, OR (b) wait for the follow-up PR that lifts
   * cancel into per-policy gating; This version keeps the current
   * cross-policy cancel for consistency with first-responder /
   * designated / consensus.
   */
  voteLocalOnly(pending, vote) {
    if (!vote.fromLoopback) {
      return this.rejectForbidden(
        pending,
        vote,
        "remote_not_allowed",
        "remote_not_allowed (local-only policy; vote not from loopback)"
      );
    }
    return this.resolveWithVote(pending, vote, {
      type: "local-only-loopback",
      resolverClientId: vote.clientId
    });
  }
  // ===========================================================
  // Shared vote-resolution helpers
  // ===========================================================
  resolveWithVote(pending, vote, decisionReason) {
    const outcome = {
      kind: "resolved",
      resolvedOptionId: vote.optionId
    };
    this.safeAudit(
      () => this.deps.audit.recordVoted(this.toRecord(pending), vote, outcome)
    );
    this.resolveEntry(
      pending,
      {
        kind: "option",
        optionId: vote.optionId,
        ...vote.metadata ? { metadata: vote.metadata } : {}
      },
      decisionReason,
      vote.clientId
    );
    return outcome;
  }
  rejectForbidden(pending, vote, reason, stderrDetail) {
    this.safeAudit(
      () => this.deps.audit.recordForbidden(this.toRecord(pending), vote, reason)
    );
    this.safeEmit(pending.sessionId, {
      type: "permission_forbidden",
      data: {
        requestId: pending.requestId,
        sessionId: pending.sessionId,
        ...vote.clientId !== void 0 ? { clientId: vote.clientId } : {},
        reason
      },
      ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
    });
    this.writeForbiddenStderr(pending, vote, stderrDetail);
    return { kind: "forbidden", reason };
  }
  // ===========================================================
  // Consensus tally helpers
  // ===========================================================
  /**
   * Compute the quorum size for a `consensus` request. Default
   * `floor(M/2) + 1` of `votersAtIssue.size`; overridden by
   * `deps.consensusQuorum` when set, capped to `M` so an operator
   * misconfig (N > M) can't deadlock.
   *
   * When the cap fires, write
   * a one-time stderr breadcrumb per request so operators don't
   * have to diff their `policy.consensusQuorum` against
   * `votersAtIssue.size` manually to understand why a quorum
   * resolved sooner than configured. Tracked on `MediatorPending`
   * so the breadcrumb fires once even though `consensusQuorumFor`
   * may be called multiple times per request (vote tally + final
   * resolution).
   */
  consensusQuorumFor(pending) {
    const m = pending.votersAtIssue.size;
    const override = this.deps.consensusQuorum;
    if (override !== void 0) {
      const capped = Math.min(override, Math.max(m, 1));
      if (capped < override && !pending.consensusQuorumCapNoted) {
        pending.consensusQuorumCapNoted = true;
        try {
          process.stderr.write(
            `permissionMediator: consensusQuorum override ${override} capped to ${capped} (votersAtIssue.size=${m}) for request ${pending.requestId} session ${pending.sessionId}
`
          );
        } catch {
        }
      }
      return capped;
    }
    return Math.max(1, Math.floor(m / 2) + 1);
  }
  totalTalliedFor(pending) {
    let total = 0;
    for (const set of pending.tallies.values()) total += set.size;
    return total;
  }
  /**
   * `votesNeeded` = `quorum - max(tally per option)`. When no
   * option has any votes (degenerate; `permission_partial_vote`
   * is only emitted AFTER the first vote, so this should never
   * appear on the wire), returns `quorum` itself. Always ≥ 1
   * because the resolved-on-quorum path returns before this
   * helper runs.
   */
  votesNeededFor(pending) {
    const quorum = this.consensusQuorumFor(pending);
    let max = 0;
    for (const set of pending.tallies.values()) {
      if (set.size > max) max = set.size;
    }
    return Math.max(quorum - max, 1);
  }
  optionTalliesFor(pending) {
    const out = {};
    for (const [optionId, set] of pending.tallies) {
      out[optionId] = set.size;
    }
    return out;
  }
  // ===========================================================
  // Resolution + cleanup
  // ===========================================================
  /**
   * Settle a pending entry. Cleanup order is hardened (cleanup-order invariant):
   *   1. clearTimeout (so a timer can never fire on a half-cleaned entry).
   *   2. Delete from `pending` (state-first half — entry no longer
   *      reachable for new votes).
   *   3. emit wire `permission_resolved` (best-effort — emit failures
   *      do not block the Promise settle). MUST come before step 4
   *      so a re-entrant subscriber synchronously casting another
   *      vote during emit sees `pending === undefined && resolved
   *      === undefined` (silent false), matching the previous ordering.
   *
   *   4. write to `resolved` (the second half of state move — late
   *      voters arriving after this see `permission_already_resolved`).
   *   5. audit.recordResolved (best-effort, same).
   *   6. Settle the Promise (LAST — callbacks running re-entrantly
   *      see consistent state).
   *
   * Previously the spec bundled
   * "delete pending + write resolved" into step 2 ahead of emit,
   * which contradicted the code. The fix
   * splits the two halves of the state move around the emit so
   * the spec faithfully describes the ordering invariant.
   *
   * @param resolverClientId  wire compat: the
   *   `permission_resolved` SSE frame stamps this as
   *   `originatorClientId`. The previous `resolvePending` in
   *   `httpAcpBridge.ts:1518-1523` filled it from the voter's
   *   trusted clientId. We preserve byte-for-byte; vote-driven
   *   paths pass `vote.clientId` (which may be undefined for
   *   loopback no-header voters); timer + session-closed paths
   *   pass undefined (no voter).
   */
  resolveEntry(pending, resolution, decisionReason, resolverClientId) {
    if (this.pending.get(pending.requestId) !== pending) {
      return;
    }
    if (pending.timer !== void 0) {
      clearTimeout(pending.timer);
      pending.timer = void 0;
    }
    this.pending.delete(pending.requestId);
    this.safeEmit(pending.sessionId, {
      type: "permission_resolved",
      data: {
        requestId: pending.requestId,
        outcome: this.toAcpOutcome(resolution),
        // Note: `voterClientId` is the canonical,
        // unambiguous name for "who cast the resolving vote". The envelope
        // `originatorClientId` below carries the SAME value for wire
        // compat (it is semantically the voter on `permission_resolved`,
        // unlike on `permission_request` where it is the prompt originator).
        // Both are optional and omitted together for no-voter resolutions
        // (timer expiry / session-closed / loopback voter with no clientId).
        ...resolverClientId !== void 0 ? { voterClientId: resolverClientId } : {}
      },
      // Preserve pre-extraction behavior: voter's clientId is stamped
      // here (not the prompt originator's). Documented inconsistency
      // with `permission_request.originatorClientId` (which IS the
      // prompt originator); we do not fix the inconsistency to
      // avoid breaking the wire shape. A4 keeps it as a deprecated
      // alias of `data.voterClientId`.
      ...resolverClientId !== void 0 ? { originatorClientId: resolverClientId } : {}
    });
    this.rememberResolved({
      requestId: pending.requestId,
      sessionId: pending.sessionId,
      resolution,
      resolverClientId
    });
    if (decisionReason !== void 0) {
      this.safeAudit(
        () => this.deps.audit.recordResolved(
          this.toRecord(pending),
          resolution,
          decisionReason
        )
      );
    }
    pending.resolve(resolution);
  }
  rememberResolved(record) {
    if (!this.resolved.has(record.requestId)) {
      this.resolvedOrder.push(record.requestId);
    }
    this.resolved.set(record.requestId, record);
    while (this.resolvedOrder.length > MAX_RESOLVED_PERMISSION_RECORDS) {
      const oldest = this.resolvedOrder.shift();
      if (oldest !== void 0) this.resolved.delete(oldest);
    }
  }
  safeEmit(sessionId, event) {
    try {
      this.deps.emit(sessionId, event);
    } catch (err) {
      try {
        process.stderr.write(
          `permissionMediator: emit failed for session=${JSON.stringify(sessionId)} type=${JSON.stringify(event.type)}: ${stringifyError(err)}
`
        );
      } catch {
      }
    }
  }
  /**
   * Emit a stderr breadcrumb
   * for every vote rejection (the three forbidden paths in
   * voteDesignated / voteConsensus / voteLocalOnly). Mirrors the
   * timeout breadcrumb pattern: audit ring + SSE event are
   * transient observability surfaces (no v1 query route, SSE drops
   * on disconnect), so an operator tailing daemon stderr would see
   * zero indication of permission rejections without this.
   *
   * Wrapped in `try/catch` because `process.stderr.write` can
   * synchronously throw on EPIPE during shutdown — a stderr
   * unavailability must not propagate up through `safeEmit` /
   * `safeAudit` and break the resolveEntry cleanup ladder. Mirrors
   * the safeEmit/safeAudit defensive posture (see the
   * matching hang scenario in safeEmit).
   */
  writeForbiddenStderr(pending, vote, reasonDetail) {
    try {
      const voterDescriptor = vote.clientId === void 0 ? "<anonymous>" : JSON.stringify(vote.clientId);
      process.stderr.write(
        `qwen serve: permission ${pending.requestId} (session ${pending.sessionId}): vote rejected (${reasonDetail}) by client ${voterDescriptor}
`
      );
    } catch {
    }
  }
  /**
   * Run an audit-publisher call defensively. The audit ring is
   * best-effort observability — a publisher exception (ring full,
   * host bug, transient I/O) MUST NOT throw out of `request()`,
   * `vote()`, or the timer callback. Without this guard, the
   * Promise the agent is awaiting would be left unsettled and the
   * pending entry would leak.
   *
   * Single helper used at all five audit call sites so the
   * "audit is best-effort" invariant is uniformly enforced (the
   * pre-fix asymmetric `try/catch` at 2 of 5 sites was a real
   * silent-failure hole.
   *
   * doc placement — JSDoc was previously
   * stacked above `writeForbiddenStderr` so IDE hover and API
   * doc generation showed the wrong attribution. Moved adjacent
   * to its actual definition.
   */
  safeAudit(fn) {
    try {
      fn();
    } catch (err) {
      try {
        process.stderr.write(
          `permissionMediator: audit publisher threw: ${stringifyError(err)}
`
        );
      } catch {
      }
    }
  }
  toRecord(pending) {
    return {
      requestId: pending.requestId,
      sessionId: pending.sessionId,
      originatorClientId: pending.originatorClientId,
      allowedOptionIds: pending.allowedOptionIds,
      issuedAtMs: pending.issuedAtMs
    };
  }
  toAcpOutcome(resolution) {
    if (resolution.kind === "option") {
      return { outcome: "selected", optionId: resolution.optionId };
    }
    return { outcome: "cancelled" };
  }
};

// packages/acp-bridge/src/bridgeClient.ts
function isFsErrorShape(err) {
  return err instanceof Error && err.name === "FsError" && typeof err.kind === "string";
}
__name(isFsErrorShape, "isFsErrorShape");
function preserveFsErrorOverAcp(err) {
  if (isFsErrorShape(err)) {
    throw new RequestError(-32603, err.message, {
      errorKind: err.kind,
      ...err.hint !== void 0 ? { hint: err.hint } : {},
      ...err.status !== void 0 ? { status: err.status } : {}
    });
  }
  throw err;
}
__name(preserveFsErrorOverAcp, "preserveFsErrorOverAcp");
function resolutionToAcpResponse(resolution) {
  if (resolution.kind === "option") {
    return {
      outcome: { outcome: "selected", optionId: resolution.optionId },
      ...resolution.metadata ?? {}
    };
  }
  return { outcome: { outcome: "cancelled" } };
}
__name(resolutionToAcpResponse, "resolutionToAcpResponse");
var MAX_EARLY_EVENT_SESSIONS = 64;
var MAX_EARLY_EVENTS_PER_SESSION = 32;
var MAX_SUGGESTION_LENGTH = 500;
var EARLY_EVENT_TTL_MS = 6e4;
var KNOWN_APPROVAL_MODES = /* @__PURE__ */ new Set([
  "plan",
  "default",
  "auto-edit",
  "auto",
  "yolo"
]);
function describeStatKind(stats) {
  if (stats.isDirectory()) return "directory";
  if (stats.isSymbolicLink()) return "symlink";
  if (stats.isCharacterDevice()) return "character device";
  if (stats.isBlockDevice()) return "block device";
  if (stats.isFIFO()) return "named pipe (FIFO)";
  if (stats.isSocket()) return "socket";
  return "non-regular file";
}
__name(describeStatKind, "describeStatKind");
function sliceLineRange(content, startLine, endLine) {
  let offset = 0;
  for (let i = 0; i < startLine; i++) {
    const nl = content.indexOf("\n", offset);
    if (nl === -1) return "";
    offset = nl + 1;
  }
  if (endLine === void 0) return content.slice(offset);
  let end = offset;
  const want = endLine - startLine;
  for (let i = 0; i < want; i++) {
    const nl = content.indexOf("\n", end);
    if (nl === -1) return content.slice(offset);
    end = nl + 1;
  }
  return content.slice(offset, end > offset ? end - 1 : end);
}
__name(sliceLineRange, "sliceLineRange");
var BridgeClient = class {
  constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted) {
    this.resolveEntry = resolveEntry;
    this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
    this.mediator = mediator;
    this.permissionTimeoutMs = permissionTimeoutMs;
    this.maxPendingPerSession = maxPendingPerSession;
    this.fileSystem = fileSystem;
    this.onModelPromoted = onModelPromoted;
    this.onModePromoted = onModePromoted;
  }
  static {
    __name(this, "BridgeClient");
  }
  async requestPermission(params) {
    const entry = this.resolveEntry(params.sessionId);
    if (!entry) return { outcome: { outcome: "cancelled" } };
    if (entry.pendingPermissionIds.size >= this.maxPendingPerSession) {
      writeStderrLine(
        `qwen serve: session ${entry.sessionId} exceeded maxPendingPermissionsPerSession (${this.maxPendingPerSession}) \u2014 resolving new permission as cancelled.`
      );
      return { outcome: { outcome: "cancelled" } };
    }
    const allowedOptionIds = new Set(
      params.options.map(
        (o) => String(o.optionId ?? "")
      )
    );
    allowedOptionIds.delete("");
    const requestId = randomUUID();
    if (allowedOptionIds.has(CANCEL_VOTE_SENTINEL)) {
      throw new CancelSentinelCollisionError(requestId, CANCEL_VOTE_SENTINEL);
    }
    const published = entry.events.publish({
      type: "permission_request",
      data: {
        requestId,
        sessionId: entry.sessionId,
        toolCall: params.toolCall,
        options: params.options
      },
      ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
    });
    if (!published) return { outcome: { outcome: "cancelled" } };
    entry.pendingPermissionIds.add(requestId);
    try {
      const record = {
        requestId,
        sessionId: entry.sessionId,
        originatorClientId: entry.activePromptOriginatorClientId,
        allowedOptionIds,
        issuedAtMs: Date.now()
      };
      const resolution = await this.mediator.request(
        record,
        this.permissionTimeoutMs
      );
      return resolutionToAcpResponse(resolution);
    } finally {
      entry.pendingPermissionIds.delete(requestId);
    }
  }
  async sessionUpdate(params) {
    const entry = this.resolveEntry(params.sessionId);
    const events = entry?.events ?? this.resolvePendingRestoreEvents(params.sessionId);
    if (!events) return;
    const originator = entry?.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {};
    const a2ui = extractA2uiToolUpdate(params);
    if (a2ui) {
      for (const surface of a2ui.surfaces) {
        events.publish({
          type: "session_update",
          data: {
            sessionId: params.sessionId,
            update: {
              sessionUpdate: "a2ui",
              a2ui: {
                surfaceId: surface.surfaceId,
                callId: a2ui.callId,
                commands: surface.commands
              },
              _meta: { serverTimestamp: Date.now(), source: "a2ui-bridge" }
            }
          },
          ...originator
        });
      }
      params = a2ui.sanitizedParams;
    }
    const updateMeta = params.update._meta;
    const originalTs = updateMeta?.["serverTimestamp"] ?? updateMeta?.["timestamp"];
    const serverTimestamp = typeof originalTs === "number" && Number.isFinite(originalTs) ? originalTs : void 0;
    events.publish({
      type: "session_update",
      data: params,
      ...originator,
      ...serverTimestamp !== void 0 ? { _meta: { serverTimestamp } } : {}
    });
  }
  /**
   * Bounded early-event buffer. Frames are keyed by sessionId; each
   * entry tracks its `expiresAt` for lazy TTL-based eviction in
   * `bufferEarlyEvent`. Drained by `drainEarlyEvents` whenever the
   * bridge registers a session with a matching id. See
   * MAX_EARLY_EVENT_* constants for capacity bounds.
   */
  earlyEvents = /* @__PURE__ */ new Map();
  /**
   * Tombstone for closed/killed session ids. Prevents late
   * `extNotification` from a dying child from leaking into the
   * early-event buffer and being replayed onto a future session
   * that reuses the same id via `session/load` or `session/resume`.
   *
   * Tombstone semantics:
   * - Marked when the bridge removes a sessionId from `byId` (kill
   *   path, channel.exited handler, closeSession).
   * - Concurrently purges any in-flight `earlyEvents[id]`.
   * - `bufferEarlyEvent` rejects tombstoned ids.
   * - `drainEarlyEvents` clears the tombstone — a fresh
   *   `createSessionEntry` for the same id is a legitimate
   *   "load/resume of a persisted session id" case.
   * - TTL = `EARLY_EVENT_TTL_MS` (60s) — same as the early-event
   *   buffer, so by the time a tombstone expires there can be no
   *   stale frame for that id anywhere in the system.
   */
  tombstonedSessionIds = /* @__PURE__ */ new Map();
  /**
   * Allow-list of sessionIds currently being restored via
   * `session/load` / `session/resume`. Bypasses the tombstone check
   * in `bufferEarlyEvent` so restore-time guardrail events for a
   * previously-closed id flow through to the future
   * `createSessionEntry -> drainEarlyEvents` call.
   *
   * Without this, the tombstone set before a future `load` can clear
   * it via `drainEarlyEvents` would silently drop legitimate
   * restore-time events (e.g. MCP discovery budget events firing
   * during the ACP call window).
   *
   * Bridge factory enters the set before awaiting the ACP restore
   * call and exits on settle (success or failure).
   */
  inFlightRestoreIds = /* @__PURE__ */ new Set();
  /**
   * Handle child->bridge ACP `extMethod` requests (calls that expect a
   * response, unlike `extNotification`). The only method served today is
   * `craft/drainMidTurnQueue`: the ACP child calls it between tool batches to
   * pull any messages the browser queued mid-turn. We splice the per-session
   * queue, return them to the child as the response, and — when non-empty —
   * publish a `mid_turn_message_injected` SSE frame so the browser can move
   * those messages out of its pending queue (a dedupe signal, not a transcript
   * render). Unknown methods reject with ACP `methodNotFound` (-32601), matching
   * the SDK's
   * default for an unimplemented client surface; the child's drain caller
   * treats that as "drain unsupported" and stops asking.
   */
  async extMethod(method, params) {
    if (method !== MID_TURN_QUEUE_DRAIN_METHOD) {
      throw RequestError.methodNotFound(method);
    }
    const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
    if (!sessionId) return { messages: [] };
    const entry = this.resolveEntry(sessionId);
    if (!entry) return { messages: [] };
    const drained = entry.midTurnMessageQueue.splice(0);
    const messages = drained.map((item) => item.text);
    if (drained.length > 0) {
      const byOriginator = /* @__PURE__ */ new Map();
      for (const item of drained) {
        const group = byOriginator.get(item.originatorClientId);
        if (group) group.push(item.text);
        else byOriginator.set(item.originatorClientId, [item.text]);
      }
      for (const [originatorClientId, texts] of byOriginator) {
        const published = entry.events.publish({
          type: MID_TURN_MESSAGE_INJECTED_EVENT,
          data: { sessionId: entry.sessionId, messages: texts },
          ...originatorClientId ? { originatorClientId } : {}
        });
        writeStderrLine(
          published ? `[mid-turn] session=${entry.sessionId} drained=${texts.length}${originatorClientId ? ` originator=${originatorClientId}` : ""} injected into running turn` : `[mid-turn] session=${entry.sessionId} drained=${texts.length} echo frame dropped (bus closed); browser may resend next turn`
        );
      }
    }
    return { messages };
  }
  /**
   * Handle child->bridge ACP `extNotification` calls. Six methods are
   * recognized — `qwen/notify/session/model-update`,
   * `qwen/notify/session/mode-update`,
   * `qwen/notify/session/title-update` (auto/in-process session titles),
   * `qwen/notify/session/prompt-suggestion` (followup assist),
   * `qwen/notify/session/terminal-sequence`, and
   * `qwen/notify/session/mcp-budget-event` — each translated into a
   * session-scoped SSE frame. Unknown methods are dropped silently
   * for forward-compat.
   */
  async extNotification(method, params) {
    if (method === "qwen/notify/session/model-update") {
      this.handleInSessionModelUpdate(params);
      return;
    }
    if (method === "qwen/notify/session/mode-update") {
      this.handleInSessionModeUpdate(params);
      return;
    }
    if (method === "qwen/notify/session/title-update") {
      const sessionId2 = params["sessionId"];
      const title = params["title"];
      if (typeof sessionId2 !== "string" || typeof title !== "string" || !title)
        return;
      const entry = this.resolveEntry(sessionId2);
      if (!entry) return;
      try {
        entry.events.publish({
          type: "session_metadata_updated",
          data: {
            sessionId: sessionId2,
            displayName: title,
            ...typeof params["titleSource"] === "string" ? { titleSource: params["titleSource"] } : {}
          }
        });
      } catch {
      }
      return;
    }
    if (method === "qwen/notify/session/prompt-suggestion") {
      const sessionId2 = params["sessionId"];
      const suggestion = params["suggestion"];
      const promptId = params["promptId"];
      if (typeof sessionId2 !== "string" || typeof suggestion !== "string" || suggestion.length === 0 || suggestion.length > MAX_SUGGESTION_LENGTH || typeof promptId !== "string") {
        writeStderrLine(
          `[demux] session=${typeof sessionId2 === "string" ? sessionId2 : "<missing>"} type=prompt_suggestion action=dropped reason=malformed`
        );
        return;
      }
      const entry = this.resolveEntry(sessionId2);
      if (!entry) return;
      entry.events.publish({
        type: "followup_suggestion",
        data: { sessionId: sessionId2, suggestion, promptId }
      });
      return;
    }
    if (method === "qwen/notify/session/terminal-sequence") {
      const sessionId2 = params["sessionId"];
      if (typeof sessionId2 !== "string") return;
      const { v: _v2, sessionId: _sid2, ...rest2 } = params;
      void _v2;
      void _sid2;
      this.publishExtNotification(sessionId2, "terminal_sequence", rest2);
      return;
    }
    if (method !== "qwen/notify/session/mcp-budget-event") return;
    const sessionId = params["sessionId"];
    if (typeof sessionId !== "string") return;
    const kind = params["kind"];
    let type;
    if (kind === "budget_warning") {
      type = "mcp_budget_warning";
    } else if (kind === "refused_batch") {
      type = "mcp_child_refused_batch";
    } else {
      return;
    }
    const { v: _v, sessionId: _sid, kind: _kind, ...rest } = params;
    void _v;
    void _sid;
    void _kind;
    this.publishExtNotification(sessionId, type, rest);
  }
  publishExtNotification(sessionId, type, data) {
    const entry = this.resolveEntry(sessionId);
    const frame = {
      type,
      data,
      ...entry?.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
    };
    if (entry) {
      entry.events.publish(frame);
      return;
    }
    this.bufferEarlyEvent(sessionId, frame);
  }
  /**
   * Promote an in-session `current_model_update` extNotification to a
   * `model_switched` bus event. Suppressed while the bridge is driving
   * its own model roundtrip (`entry.modelRoundtripInFlight`) — there the
   * bridge publishes the authoritative `model_switched`, so promoting
   * here too would double-publish. A structured log records the decision
   * so the `dropped` case is observable.
   */
  handleInSessionModelUpdate(params) {
    const sessionId = params["sessionId"];
    const currentModelId = params["currentModelId"];
    if (typeof sessionId !== "string" || typeof currentModelId !== "string") {
      return;
    }
    const entry = this.resolveEntry(sessionId);
    if (!entry) {
      writeStderrLine(
        `[demux] session=${sessionId} type=current_model_update action=dropped reason=no_entry`
      );
      return;
    }
    if (entry.modelRoundtripInFlight) {
      writeStderrLine(
        `[demux] session=${sessionId} type=current_model_update action=suppressed reason=bridge_roundtrip_in_flight`
      );
      return;
    }
    if (this.onModelPromoted) {
      this.onModelPromoted(
        entry,
        currentModelId,
        entry.activePromptOriginatorClientId
      );
    } else {
      entry.events.publish({
        type: "model_switched",
        data: { sessionId, modelId: currentModelId },
        ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
      });
    }
    writeStderrLine(
      `[demux] session=${sessionId} type=current_model_update action=promoted model=${currentModelId}`
    );
  }
  /**
   * A2: promote an in-session `current_mode_update` extNotification to
   * `approval_mode_changed`. Uses the same suppression pattern as
   * `handleInSessionModelUpdate` — suppressed while the bridge is driving
   * its own approval-mode roundtrip (`entry.approvalModeRoundtripInFlight`)
   * — but diverges with two additions the model handler lacks: enum
   * validation against `KNOWN_APPROVAL_MODES`, and a legacy
   * `session_update{current_mode_update}` dual-emit for IDE companion
   * compat (transition — see §6 of the design doc), itself deduped via the
   * `legacyFrameSent` flag.
   */
  handleInSessionModeUpdate(params) {
    const sessionId = params["sessionId"];
    const currentModeId = params["currentModeId"];
    if (typeof sessionId !== "string" || typeof currentModeId !== "string") {
      return;
    }
    if (!KNOWN_APPROVAL_MODES.has(currentModeId)) {
      writeStderrLine(
        `[demux] session=${sessionId} type=current_mode_update action=dropped reason=unknown_mode mode=${currentModeId}`
      );
      return;
    }
    const entry = this.resolveEntry(sessionId);
    if (!entry) {
      writeStderrLine(
        `[demux] session=${sessionId} type=current_mode_update action=dropped reason=no_entry`
      );
      return;
    }
    if (entry.approvalModeRoundtripInFlight) {
      writeStderrLine(
        `[demux] session=${sessionId} type=current_mode_update action=suppressed reason=bridge_roundtrip_in_flight`
      );
      return;
    }
    if (this.onModePromoted) {
      this.onModePromoted(
        entry,
        currentModeId,
        entry.activePromptOriginatorClientId
      );
    } else {
      entry.events.publish({
        type: "approval_mode_changed",
        data: {
          sessionId,
          previous: "default",
          next: currentModeId,
          persisted: false
        },
        ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
      });
    }
    if (params["legacyFrameSent"] === true) {
      writeStderrLine(
        `[demux] session=${sessionId} type=current_mode_update action=promoted mode=${currentModeId} legacy_frame=skipped`
      );
      return;
    }
    entry.events.publish({
      type: "session_update",
      data: {
        sessionId,
        update: {
          sessionUpdate: "current_mode_update",
          currentModeId
        }
      },
      ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
    });
    writeStderrLine(
      `[demux] session=${sessionId} type=current_mode_update action=promoted mode=${currentModeId}`
    );
  }
  /**
   * Enqueue `frame` for `sessionId`. Lazy TTL sweep runs first so
   * caller doesn't pay for stale entries before deciding whether
   * the session-cap is reached. New sessionIds past
   * `MAX_EARLY_EVENT_SESSIONS` are dropped (defense against a
   * malicious / buggy child fanning out fake sessionIds); same-
   * sessionId frames past `MAX_EARLY_EVENTS_PER_SESSION` are
   * dropped to bound per-session memory.
   */
  bufferEarlyEvent(sessionId, frame) {
    const now = Date.now();
    this.sweepExpiredTombstones(now);
    if (this.tombstonedSessionIds.has(sessionId) && !this.inFlightRestoreIds.has(sessionId)) {
      writeStderrLine(
        `qwen serve: dropping mcp guardrail extNotification for tombstoned session ${JSON.stringify(sessionId)} (post-close stale event)`
      );
      return;
    }
    this.sweepExpiredEarlyEvents(now);
    let buf = this.earlyEvents.get(sessionId);
    if (!buf) {
      if (this.earlyEvents.size >= MAX_EARLY_EVENT_SESSIONS) {
        writeStderrLine(
          `qwen serve: dropping mcp guardrail extNotification \u2014 early-event buffer at MAX_EARLY_EVENT_SESSIONS (${MAX_EARLY_EVENT_SESSIONS}); possible session-id fanout abuse`
        );
        return;
      }
      buf = { frames: [], expiresAt: now + EARLY_EVENT_TTL_MS };
      this.earlyEvents.set(sessionId, buf);
    }
    if (buf.frames.length >= MAX_EARLY_EVENTS_PER_SESSION) {
      writeStderrLine(
        `qwen serve: dropping mcp guardrail extNotification for session ${JSON.stringify(sessionId)} \u2014 per-session cap (${MAX_EARLY_EVENTS_PER_SESSION}) reached`
      );
      return;
    }
    buf.frames.push(frame);
  }
  sweepExpiredEarlyEvents(now) {
    for (const [sid, buf] of this.earlyEvents) {
      if (buf.expiresAt <= now) this.earlyEvents.delete(sid);
    }
  }
  sweepExpiredTombstones(now) {
    for (const [sid, expiresAt] of this.tombstonedSessionIds) {
      if (expiresAt <= now) this.tombstonedSessionIds.delete(sid);
    }
  }
  /**
   * Mark a sessionId as closed so a late `extNotification` from the
   * dying child can't leak into the early-event buffer. Bridge factory
   * calls this from every `byId.delete(sid)` site (kill path,
   * channel.exited handler, closeSession). Idempotent on already-
   * tombstoned ids — refreshes the TTL so a recently-killed id stays
   * dead long enough for any in-flight stale frames to expire.
   */
  markSessionClosed(sessionId) {
    const now = Date.now();
    this.sweepExpiredTombstones(now);
    this.tombstonedSessionIds.set(sessionId, now + EARLY_EVENT_TTL_MS);
    this.earlyEvents.delete(sessionId);
  }
  /**
   * Mark a sessionId as currently being restored via `session/load` /
   * `session/resume`. While in this set, `bufferEarlyEvent` accepts
   * frames for the id even if it's tombstoned — so restore-time
   * guardrail events from the freshly-restored child reach
   * `drainEarlyEvents` instead of being rejected by the tombstone.
   *
   * Bridge factory calls this BEFORE awaiting the ACP restore call.
   * `clearRestoreInFlight` is paired in the matching `finally` so a
   * failed restore doesn't leave a dangling allow-list entry.
   */
  markRestoreInFlight(sessionId) {
    this.inFlightRestoreIds.add(sessionId);
  }
  /**
   * Companion to `markRestoreInFlight`. Bridge factory calls this when
   * the restore IIFE settles — after `createSessionEntry` runs
   * (success) or after the ACP restore call fails (error). Cleared to
   * prevent the Set from growing forever under high restore churn.
   */
  clearRestoreInFlight(sessionId) {
    this.inFlightRestoreIds.delete(sessionId);
  }
  /**
   * Drain any frames buffered for `sessionId` onto `entry.events`.
   * Bridge calls this immediately after `byId.set(sessionId, entry)`
   * in `createSessionEntry`. The frames were captured before the
   * entry existed (e.g. MCP discovery during the child's `newSession`
   * handler), so draining them now lands them in the replay ring as
   * the FIRST events of this session.
   *
   * Public so the bridge factory can call it directly. Idempotent on
   * unknown sessionIds.
   */
  drainEarlyEvents(sessionId, entry) {
    this.tombstonedSessionIds.delete(sessionId);
    const buf = this.earlyEvents.get(sessionId);
    if (!buf) return;
    for (const frame of buf.frames) entry.events.publish(frame);
    this.earlyEvents.delete(sessionId);
  }
  async writeTextFile(params) {
    if (this.fileSystem) {
      try {
        return await this.fileSystem.writeText(params);
      } catch (err) {
        preserveFsErrorOverAcp(err);
      }
    }
    let realTarget = params.path;
    try {
      realTarget = await fs.realpath(params.path);
    } catch (err) {
      const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
      if (code !== "ENOENT") throw err;
      try {
        const linkTarget = await fs.readlink(params.path);
        realTarget = path.resolve(path.dirname(params.path), linkTarget);
      } catch {
      }
    }
    const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
    let preserveMode;
    try {
      const targetStat = await fs.stat(realTarget);
      preserveMode = {
        mode: targetStat.mode & 4095,
        uid: targetStat.uid,
        gid: targetStat.gid
      };
    } catch (err) {
      const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
      if (code !== "ENOENT") throw err;
    }
    try {
      await fs.writeFile(tmp, params.content, {
        encoding: "utf8",
        flag: "wx",
        mode: preserveMode?.mode ?? 384
      });
      if (preserveMode) {
        await fs.chmod(tmp, preserveMode.mode).catch(() => {
        });
        await fs.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
        });
      }
      await fs.rename(tmp, realTarget);
    } catch (err) {
      await fs.unlink(tmp).catch(() => {
      });
      throw err;
    }
    return {};
  }
  async readTextFile(params) {
    if (this.fileSystem) {
      try {
        return await this.fileSystem.readText(params);
      } catch (err) {
        preserveFsErrorOverAcp(err);
      }
    }
    if (typeof params.limit === "number" && params.limit <= 0) {
      return { content: "" };
    }
    const READ_FILE_SIZE_CAP = 100 * 1024 * 1024;
    const stats = await fs.stat(params.path);
    if (!stats.isFile()) {
      throw new Error(
        `readTextFile: ${params.path} is not a regular file (reported as ${describeStatKind(stats)}). Pipe / device / proc-like inputs can produce unbounded data and aren't supported by the bridge fs proxy.`
      );
    }
    if (stats.size > READ_FILE_SIZE_CAP) {
      throw new Error(
        `readTextFile: ${params.path} is ${stats.size} bytes, exceeds the ${READ_FILE_SIZE_CAP}-byte daemon cap. Tail/grep externally and feed the relevant slice instead.`
      );
    }
    const content = await fs.readFile(params.path, "utf8");
    if (typeof params.line === "number" || typeof params.limit === "number") {
      const startLine = params.line ?? 1;
      const start = startLine > 0 ? startLine - 1 : 0;
      const end = params.limit != null ? start + params.limit : void 0;
      return { content: sliceLineRange(content, start, end) };
    }
    return { content };
  }
};
var A2UI_TOOL_RE = /(^|__)(present_ui|present_choices|a2ui_action)$/;
function isA2uiToolMeta(meta) {
  if (!meta) return false;
  if (typeof meta.serverId === "string" && meta.serverId.toLowerCase().includes("a2ui"))
    return true;
  return typeof meta.toolName === "string" && A2UI_TOOL_RE.test(meta.toolName);
}
__name(isA2uiToolMeta, "isA2uiToolMeta");
function splitA2uiText(raw) {
  const s = raw.replace(/^\s+/, "");
  if (s[0] !== "[") return null;
  let depth = 0;
  let inStr = false;
  let esc = false;
  let end = -1;
  for (let i = 0; i < s.length; i++) {
    const c = s[i];
    if (inStr) {
      if (esc) esc = false;
      else if (c === "\\") esc = true;
      else if (c === '"') inStr = false;
      continue;
    }
    if (c === '"') inStr = true;
    else if (c === "[") depth++;
    else if (c === "]") {
      depth--;
      if (depth === 0) {
        end = i + 1;
        break;
      }
    }
  }
  if (end < 0) return null;
  try {
    const arr = JSON.parse(s.slice(0, end));
    if (!Array.isArray(arr) || arr.length === 0) return null;
    return [arr, s.slice(end).trim()];
  } catch {
    return null;
  }
}
__name(splitA2uiText, "splitA2uiText");
function surfaceIdOf(c) {
  const cmd = c;
  return cmd?.createSurface?.surfaceId ?? cmd?.updateComponents?.surfaceId ?? cmd?.updateDataModel?.surfaceId ?? cmd?.deleteSurface?.surfaceId;
}
__name(surfaceIdOf, "surfaceIdOf");
function extractA2uiToolUpdate(params) {
  const update = params.update;
  if (!update || update["sessionUpdate"] !== "tool_call_update") return null;
  const meta = update["_meta"];
  if (!isA2uiToolMeta(meta)) return null;
  const content = update["content"];
  if (!Array.isArray(content)) return null;
  let split = null;
  let hitIndex = -1;
  for (let i = 0; i < content.length; i++) {
    const inner = content[i]?.content;
    if (typeof inner?.text === "string") {
      split = splitA2uiText(inner.text);
      if (split) {
        hitIndex = i;
        break;
      }
    }
  }
  if (!split) return null;
  const [commands, fallback] = split;
  const order = [];
  const grouped = /* @__PURE__ */ new Map();
  for (const c of commands) {
    const sid = surfaceIdOf(c);
    if (!sid) {
      const shape = c && typeof c === "object" ? Object.keys(c).join(",") || "empty object" : typeof c;
      writeStderrLine(
        `a2ui: dropping command with unrecognized shape (${shape})`
      );
      continue;
    }
    if (!grouped.has(sid)) {
      grouped.set(sid, []);
      order.push(sid);
    }
    grouped.get(sid).push(c);
  }
  const surfaces = order.map((sid) => ({
    surfaceId: sid,
    commands: grouped.get(sid)
  }));
  const sanitizedText = fallback || "[A2UI surface rendered]";
  const sanitizedContent = content.map(
    (block, i) => i === hitIndex ? {
      ...block,
      content: {
        ...block.content ?? {},
        text: sanitizedText
      }
    } : block
  );
  const sanitizedUpdate = {
    ...update,
    content: sanitizedContent
  };
  if (typeof update["rawOutput"] === "string") {
    sanitizedUpdate["rawOutput"] = sanitizedText;
  }
  return {
    surfaces,
    callId: typeof update["toolCallId"] === "string" ? update["toolCallId"] : void 0,
    sanitizedParams: {
      ...params,
      update: sanitizedUpdate
    }
  };
}
__name(extractA2uiToolUpdate, "extractA2uiToolUpdate");

// packages/acp-bridge/src/bridge.ts
var NOOP_BRIDGE_TELEMETRY = {
  captureContext: /* @__PURE__ */ __name(() => void 0, "captureContext"),
  runWithContext(_captured, fn) {
    return fn();
  },
  withSpan(_operation, _attributes, fn) {
    return fn();
  },
  event() {
  },
  injectPromptContext(request) {
    const meta = request._meta;
    if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
      return request;
    }
    const record = meta;
    if (!(DAEMON_TRACEPARENT_META_KEY in record) && !(DAEMON_TRACESTATE_META_KEY in record)) {
      return request;
    }
    const nextMeta = { ...record };
    delete nextMeta[DAEMON_TRACEPARENT_META_KEY];
    delete nextMeta[DAEMON_TRACESTATE_META_KEY];
    return { ...request, _meta: nextMeta };
  }
};
function isServeDebugLoggingEnabled() {
  const value = process.env["QWEN_SERVE_DEBUG"];
  if (!value) return false;
  return !["0", "false", "off", "no"].includes(value.trim().toLowerCase());
}
__name(isServeDebugLoggingEnabled, "isServeDebugLoggingEnabled");
function writeServeDebugLine(message) {
  if (!isServeDebugLoggingEnabled()) return;
  writeStderrLine(`qwen serve debug: ${message}`);
}
__name(writeServeDebugLine, "writeServeDebugLine");
var MAX_DISPLAY_NAME_LENGTH = 256;
var MAX_ECHO_CONTENT_BLOCKS = 256;
function extractPermissionResponseMetadata(response) {
  if (response === null || typeof response !== "object") return void 0;
  const answers = response.answers;
  if (answers !== null && typeof answers === "object" && !Array.isArray(answers)) {
    const entries = Object.entries(answers);
    if (entries.every(([, v]) => typeof v === "string")) {
      return { answers };
    }
  }
  return void 0;
}
__name(extractPermissionResponseMetadata, "extractPermissionResponseMetadata");
function echoPromptToSessionBus(entry, req, originatorClientId) {
  const prompt = req.prompt;
  if (!Array.isArray(prompt) || prompt.length === 0) return;
  const serverTimestamp = Date.now();
  const blockCount = Math.min(prompt.length, MAX_ECHO_CONTENT_BLOCKS);
  for (let i = 0; i < blockCount; i += 1) {
    const part = prompt[i];
    if (!part || typeof part !== "object" || Array.isArray(part)) continue;
    try {
      entry.events.publish({
        type: "session_update",
        data: {
          sessionId: req.sessionId,
          update: {
            sessionUpdate: "user_message_chunk",
            content: part,
            // `_meta` lives inside the `update` object rather than at
            // envelope level. `_meta` is a standard JSON-RPC/MCP extension
            // field permitted alongside spec fields, the SDK normalizer
            // reads it from `update._meta`/`data._meta`, and every other
            // agent-emitted session_update carries `_meta` the same way.
            _meta: { serverTimestamp, source: "bridge-echo" }
          }
        },
        ...originatorClientId ? { originatorClientId } : {}
      });
    } catch {
    }
  }
}
__name(echoPromptToSessionBus, "echoPromptToSessionBus");
function broadcastPromptCancelled(entry, sessionId, originatorClientId, reason) {
  try {
    entry.events.publish({
      type: "prompt_cancelled",
      data: { sessionId, ...reason ? { reason } : {} },
      ...originatorClientId ? { originatorClientId } : {}
    });
  } catch {
  }
}
__name(broadcastPromptCancelled, "broadcastPromptCancelled");
function broadcastPromptCancelledOnce(entry, sessionId, originatorClientId, reason) {
  if (entry.cancelBroadcast) {
    writeStderrLine(
      `broadcastPromptCancelledOnce: suppressed duplicate cancel for session ${sessionId} (latch already set)`
    );
    return;
  }
  entry.cancelBroadcast = true;
  broadcastPromptCancelled(entry, sessionId, originatorClientId, reason);
}
__name(broadcastPromptCancelledOnce, "broadcastPromptCancelledOnce");
function broadcastTurnComplete(entry, sessionId, promptResult, promptId, originatorClientId) {
  entry.events.publish({
    type: "turn_complete",
    data: {
      sessionId,
      stopReason: promptResult.stopReason ?? "end_turn",
      ...promptId ? { promptId } : {}
    },
    ...originatorClientId ? { originatorClientId } : {}
  });
}
__name(broadcastTurnComplete, "broadcastTurnComplete");
function extractErrorMessage(err) {
  if (err instanceof Error) {
    const data = err.data;
    const detail = extractJsonRpcErrorDetail(data);
    return detail ?? err.message;
  }
  if (typeof err === "object" && err !== null) {
    const obj = err;
    const detail = extractJsonRpcErrorDetail(obj["data"]);
    if (detail) return detail;
    const msg = obj["message"];
    if (typeof msg === "string") return msg;
  }
  return String(err);
}
__name(extractErrorMessage, "extractErrorMessage");
function extractJsonRpcErrorDetail(data) {
  if (typeof data === "string" && data.length > 0) return data;
  if (typeof data === "object" && data !== null) {
    const details = data["details"];
    if (typeof details === "string" && details.length > 0) return details;
    const message = data["message"];
    if (typeof message === "string" && message.length > 0) return message;
  }
  return void 0;
}
__name(extractJsonRpcErrorDetail, "extractJsonRpcErrorDetail");
function extractErrorCode(err) {
  if (typeof err !== "object" || err === null || !("code" in err))
    return void 0;
  const raw = err["code"];
  if (typeof raw === "string") return raw;
  if (typeof raw === "number") return String(raw);
  return void 0;
}
__name(extractErrorCode, "extractErrorCode");
function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId) {
  const message = extractErrorMessage(err);
  const code = extractErrorCode(err);
  entry.retryAllowed = true;
  entry.events.publish({
    type: "turn_error",
    data: {
      sessionId,
      message,
      ...code ? { code } : {},
      ...promptId ? { promptId } : {}
    },
    ...originatorClientId ? { originatorClientId } : {}
  });
}
__name(broadcastTurnError, "broadcastTurnError");
function hasControlCharacter(value) {
  for (let i = 0; i < value.length; i += 1) {
    const code = value.charCodeAt(i);
    if (code <= 31 || code === 127) {
      return true;
    }
  }
  return false;
}
__name(hasControlCharacter, "hasControlCharacter");
var DEFAULT_INIT_TIMEOUT_MS = 1e4;
var PERSIST_TIMEOUT_MS = 5e3;
var MCP_RESTART_TIMEOUT_MS = 3e5;
var MCP_OAUTH_TIMEOUT_MS = 6e5;
var DAEMON_RETRY_META_KEY = "qwen.daemon.retry";
var SESSION_RECAP_TIMEOUT_MS = 6e4;
var SESSION_BTW_TIMEOUT_MS = 6e4;
var SHELL_COMMAND_TIMEOUT_MS = 12e4;
var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
var MAX_MID_TURN_QUEUE_DEPTH = 20;
var DEFAULT_MAX_SESSIONS = 20;
var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
var MAX_EVENT_RING_SIZE = 1e6;
var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
var DEFAULT_MAX_PENDING_PER_SESSION = 64;
var DEFAULT_SESSION_REAP_INTERVAL_MS = 6e4;
var DEFAULT_SESSION_IDLE_TIMEOUT_MS = 30 * 6e4;
function createAcpSessionBridge(opts) {
  const defaultSessionScope = opts.sessionScope ?? "single";
  let maxSessions;
  if (opts.maxSessions === void 0) {
    maxSessions = DEFAULT_MAX_SESSIONS;
  } else if (Number.isNaN(opts.maxSessions)) {
    throw new TypeError(
      `Invalid maxSessions: NaN. Must be a number >= 0 (0 / Infinity = unlimited).`
    );
  } else if (opts.maxSessions < 0) {
    throw new TypeError(
      `Invalid maxSessions: ${opts.maxSessions}. Must be >= 0 (0 / Infinity = unlimited).`
    );
  } else if (opts.maxSessions === 0 || opts.maxSessions === Infinity) {
    maxSessions = Infinity;
  } else {
    maxSessions = opts.maxSessions;
  }
  if (defaultSessionScope !== "single" && defaultSessionScope !== "thread") {
    throw new TypeError(
      `Invalid sessionScope: ${JSON.stringify(defaultSessionScope)}. Expected 'single' or 'thread'.`
    );
  }
  const eventRingSize = opts.eventRingSize ?? DEFAULT_RING_SIZE;
  if (!Number.isInteger(eventRingSize) || eventRingSize < 1 || eventRingSize > MAX_EVENT_RING_SIZE) {
    throw new TypeError(
      `Invalid eventRingSize: ${opts.eventRingSize}. Must be a positive integer in [1, ${MAX_EVENT_RING_SIZE}].`
    );
  }
  const channelFactory = opts.channelFactory ?? defaultSpawnChannelFactory;
  const childEnvOverrides = opts.childEnvOverrides ? Object.freeze({ ...opts.childEnvOverrides }) : Object.freeze({});
  const initTimeoutMs = opts.initializeTimeoutMs ?? DEFAULT_INIT_TIMEOUT_MS;
  if (initTimeoutMs <= 0) {
    throw new TypeError(
      `Invalid initializeTimeoutMs: ${initTimeoutMs}. Must be > 0.`
    );
  }
  const permissionTimeoutRaw = opts.permissionResponseTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
  const permissionTimeoutMs = permissionTimeoutRaw > 0 && Number.isFinite(permissionTimeoutRaw) ? (
    // Clamp to 2^31-1: Node treats setTimeout delays larger than
    // this as 1ms (TimeoutOverflowWarning), which would make a
    // huge "effectively never" timeout cancel prompts almost
    // immediately — the opposite of intent. Mirrors the sibling
    // `resolvePositiveFiniteMs` / `resolvedChannelIdleTimeoutMs`.
    Math.min(permissionTimeoutRaw, 2147483647)
  ) : 0;
  const maxPendingRaw = opts.maxPendingPermissionsPerSession ?? DEFAULT_MAX_PENDING_PER_SESSION;
  const maxPendingPerSession = maxPendingRaw > 0 && Number.isFinite(maxPendingRaw) ? maxPendingRaw : Infinity;
  const maxPendingPromptsRaw = opts.maxPendingPromptsPerSession ?? DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION;
  let maxPendingPromptsPerSession;
  if (maxPendingPromptsRaw === 0 || maxPendingPromptsRaw === Number.POSITIVE_INFINITY) {
    maxPendingPromptsPerSession = Infinity;
  } else if (!Number.isInteger(maxPendingPromptsRaw) || maxPendingPromptsRaw < 0) {
    throw new TypeError(
      `Invalid maxPendingPromptsPerSession: ${maxPendingPromptsRaw}. Must be a non-negative integer (0 / Infinity = unlimited).`
    );
  } else {
    maxPendingPromptsPerSession = maxPendingPromptsRaw;
  }
  if (!path2.isAbsolute(opts.boundWorkspace)) {
    throw new TypeError(
      `Invalid boundWorkspace: "${opts.boundWorkspace}". Must be an absolute path.`
    );
  }
  const boundWorkspace = opts.boundWorkspace;
  const persistApprovalMode = opts.persistApprovalMode;
  const telemetry = opts.telemetry ?? NOOP_BRIDGE_TELEMETRY;
  let defaultEntry;
  let channelInfo;
  let idleTimer;
  const sessionReapIntervalMs = resolvePositiveFiniteMs(
    opts.sessionReapIntervalMs,
    DEFAULT_SESSION_REAP_INTERVAL_MS
  );
  const sessionIdleTimeoutMs = resolvePositiveFiniteMs(
    opts.sessionIdleTimeoutMs,
    DEFAULT_SESSION_IDLE_TIMEOUT_MS
  );
  let sessionReaper;
  let lastActivityTimestamp = null;
  let activePromptCounter = 0;
  function touchActivity() {
    lastActivityTimestamp = Date.now();
  }
  __name(touchActivity, "touchActivity");
  function resolvePositiveFiniteMs(raw, fallback) {
    if (raw === void 0) return fallback;
    return raw > 0 && Number.isFinite(raw) ? Math.min(raw, 2147483647) : 0;
  }
  __name(resolvePositiveFiniteMs, "resolvePositiveFiniteMs");
  function cancelIdleTimer() {
    if (idleTimer !== void 0) {
      clearTimeout(idleTimer);
      idleTimer = void 0;
    }
  }
  __name(cancelIdleTimer, "cancelIdleTimer");
  async function killChannelWithLog(ci, context) {
    ci.isDying = true;
    await ci.channel.kill().catch((err) => {
      writeStderrLine(
        `qwen serve: channel kill failed${context ? ` (${context})` : ""}: ${String(err)}`
      );
    });
  }
  __name(killChannelWithLog, "killChannelWithLog");
  function resolvedChannelIdleTimeoutMs() {
    const raw = opts.channelIdleTimeoutMs;
    return raw !== void 0 && Number.isFinite(raw) && raw > 0 ? Math.min(raw, 2147483647) : 0;
  }
  __name(resolvedChannelIdleTimeoutMs, "resolvedChannelIdleTimeoutMs");
  async function startIdleTimer(ci, context) {
    const timeoutMs = resolvedChannelIdleTimeoutMs();
    if (timeoutMs <= 0) {
      await killChannelWithLog(ci, context);
      return;
    }
    cancelIdleTimer();
    idleTimer = setTimeout(() => {
      idleTimer = void 0;
      if (ci.sessionIds.size === 0 && ci.pendingRestoreIds.size === 0) {
        writeStderrLine(
          `qwen serve: idle timeout (${timeoutMs}ms) expired, killing channel`
        );
        void killChannelWithLog(ci, "idle timeout");
      }
    }, timeoutMs);
    idleTimer.unref();
  }
  __name(startIdleTimer, "startIdleTimer");
  function startSessionReaper() {
    if (sessionReapIntervalMs <= 0 || sessionIdleTimeoutMs <= 0) {
      writeStderrLine("qwen serve: session reaper disabled");
      return;
    }
    writeStderrLine(
      `qwen serve: session reaper started (interval ${sessionReapIntervalMs}ms, idle threshold ${sessionIdleTimeoutMs}ms)`
    );
    sessionReaper = setInterval(() => {
      if (shuttingDown) return;
      const now = Date.now();
      for (const [id, entry] of byId) {
        if (entry.promptActive) continue;
        if (entry.events.subscriberCount > 0) continue;
        const lastActive = entry.sessionLastSeenAt ?? Date.parse(entry.createdAt);
        const idle = now - lastActive;
        if (idle < sessionIdleTimeoutMs) continue;
        writeStderrLine(
          `qwen serve: reaping idle session ${JSON.stringify(id)} (idle for ${Math.round(idle / 1e3)}s, threshold ${Math.round(sessionIdleTimeoutMs / 1e3)}s)`
        );
        void closeSessionImpl(id, void 0, { reason: "idle_timeout" }).catch(
          (err) => {
            writeStderrLine(
              `qwen serve: session reaper failed to close ${JSON.stringify(id)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
            );
          }
        );
      }
    }, sessionReapIntervalMs);
    sessionReaper.unref();
  }
  __name(startSessionReaper, "startSessionReaper");
  function stopSessionReaper() {
    if (sessionReaper !== void 0) {
      clearInterval(sessionReaper);
      sessionReaper = void 0;
    }
  }
  __name(stopSessionReaper, "stopSessionReaper");
  const aliveChannels = /* @__PURE__ */ new Set();
  let inFlightChannelSpawn;
  const byId = /* @__PURE__ */ new Map();
  const permissionConsensusQuorum = opts.permissionConsensusQuorum;
  if (permissionConsensusQuorum !== void 0 && (!Number.isInteger(permissionConsensusQuorum) || permissionConsensusQuorum < 1)) {
    throw new Error(
      `BridgeOptions.permissionConsensusQuorum must be a positive integer; got ${String(permissionConsensusQuorum)}`
    );
  }
  const permissionAudit = opts.permissionAudit ?? createNoOpPermissionAuditPublisher();
  const permissionMediator = new MultiClientPermissionMediator(
    opts.permissionPolicy ?? "first-responder",
    {
      emit: /* @__PURE__ */ __name((sessionId, event) => {
        const sessionEntry = byId.get(sessionId);
        sessionEntry?.events.publish(event);
      }, "emit"),
      audit: permissionAudit,
      ...permissionConsensusQuorum !== void 0 ? { consensusQuorum: permissionConsensusQuorum } : {},
      now: /* @__PURE__ */ __name(() => Date.now(), "now"),
      votersForSession: /* @__PURE__ */ __name((sessionId) => {
        const sessionEntry = byId.get(sessionId);
        if (!sessionEntry) return /* @__PURE__ */ new Set();
        return new Set(sessionEntry.clientIds.keys());
      }, "votersForSession")
    }
  );
  let shuttingDown = false;
  const teeServeDebugLine = /* @__PURE__ */ __name((message) => {
    writeServeDebugLine(message);
    if (opts.onDiagnosticLine && isServeDebugLoggingEnabled()) {
      opts.onDiagnosticLine(`qwen serve debug: ${message}`, "info");
    }
  }, "teeServeDebugLine");
  const inFlightSpawns = /* @__PURE__ */ new Map();
  const inFlightRestores = /* @__PURE__ */ new Map();
  const pendingRestoreEvents = /* @__PURE__ */ new Map();
  const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID2()}`, "createClientId");
  const registerClient = /* @__PURE__ */ __name((entry, requestedClientId) => {
    if (requestedClientId && entry.clientIds.has(requestedClientId)) {
      entry.clientIds.set(
        requestedClientId,
        (entry.clientIds.get(requestedClientId) ?? 0) + 1
      );
      return requestedClientId;
    }
    const clientId = createClientId();
    entry.clientIds.set(clientId, 1);
    return clientId;
  }, "registerClient");
  const unregisterClient = /* @__PURE__ */ __name((entry, clientId) => {
    if (clientId === void 0) return;
    const count = entry.clientIds.get(clientId);
    if (count === void 0) return;
    if (count <= 1) {
      entry.clientIds.delete(clientId);
      entry.clientLastSeenAt.delete(clientId);
    } else {
      entry.clientIds.set(clientId, count - 1);
    }
  }, "unregisterClient");
  const resolveTrustedClientId = /* @__PURE__ */ __name((entry, clientId) => {
    if (clientId === void 0) return void 0;
    if (!entry.clientIds.has(clientId)) {
      throw new InvalidClientIdError(entry.sessionId, clientId);
    }
    return clientId;
  }, "resolveTrustedClientId");
  async function ensureChannel() {
    cancelIdleTimer();
    if (channelInfo && !channelInfo.isDying) return channelInfo;
    if (inFlightChannelSpawn) return await inFlightChannelSpawn;
    const promise = (async () => {
      const channel = await telemetry.withSpan(
        "channel.spawn",
        {
          "qwen-code.daemon.bridge.operation": "channel.spawn",
          "qwen-code.daemon.channel.reused": false
        },
        async () => await channelFactory(boundWorkspace, childEnvOverrides)
      );
      const client = new BridgeClient(
        // BfFut: ACP today carries a sessionId on every per-session
        // notification / request, so the no-sessionId branch is
        // technically unreachable. But the channel is multi-session
        // (Stage 1.5 multiplex), so if ACP ever grows a no-sessionId
        // call we'd silently drop it on a multi-session channel
        // instead of throwing. Surface that ambiguity loudly.
        (sessionId) => {
          if (sessionId) return byId.get(sessionId);
          if (channelInfo && channelInfo.sessionIds.size > 1) {
            throw new Error(
              "BridgeClient: ACP call without sessionId on a multi-session channel cannot be routed \u2014 workspace=" + boundWorkspace
            );
          }
          return void 0;
        },
        (sessionId) => sessionId ? pendingRestoreEvents.get(sessionId) : void 0,
        permissionMediator,
        permissionTimeoutMs,
        maxPendingPerSession,
        // Forward the optional `BridgeFileSystem` injection so
        // production `qwen serve` can wire the `WorkspaceFileSystem`
        // adapter into BridgeClient's fs proxy methods. Tests + Mode A
        // consumers + channels / IDE companion omit it; BridgeClient
        // falls back to its inline fs proxy.
        opts.fileSystem,
        // §2.3: centralised model_switched publish — keeps cache + generation
        // update atomic. BridgeClient calls this instead of inlining publish.
        (entry, modelId, originator) => publishModelSwitched(entry, modelId, originator),
        // A2: centralised approval_mode_changed publish on in-session mode
        // promotion. `previous` is read from the bridge state cache.
        (entry, modeId, originator) => {
          const se = entry;
          publishApprovalModeChanged(
            se,
            {
              previous: se.currentApprovalMode ?? "default",
              next: modeId,
              persisted: false
            },
            originator
          );
        }
      );
      const connection = new ClientSideConnection(() => client, channel.stream);
      const info = {
        channel,
        connection,
        client,
        sessionIds: /* @__PURE__ */ new Set(),
        pendingRestoreIds: /* @__PURE__ */ new Set(),
        isDying: false,
        handshakeComplete: false
      };
      aliveChannels.add(info);
      if (aliveChannels.size > 2) {
        writeStderrLine(
          `qwen serve: WARNING aliveChannels.size=${aliveChannels.size} (expected 1, max 2 during killSession-then-spawnOrAttach overlap) \u2014 possible channel leak; check that prior channels' channel.exited fired and the handler ran cleanup.`
        );
      }
      void channel.exited.then((exitInfo) => {
        if (channelInfo === info) cancelIdleTimer();
        aliveChannels.delete(info);
        if (channelInfo === info) channelInfo = void 0;
        const sessions = Array.from(info.sessionIds);
        info.sessionIds.clear();
        const channelExitExpected = shuttingDown || info.isDying;
        if (info.handshakeComplete) {
          telemetry.metrics?.channelLifecycle("exit", channelExitExpected);
        }
        if (!shuttingDown) {
          telemetry.event("channel.exited", {
            "qwen-code.daemon.channel.exit_code": exitInfo?.exitCode ?? -1,
            "qwen-code.daemon.channel.session_count": sessions.length,
            ...exitInfo?.signalCode ? { "qwen-code.daemon.channel.signal": exitInfo.signalCode } : {}
          });
          writeStderrLine(
            `qwen serve: channel exited (code=${exitInfo?.exitCode ?? "none"}, signal=${exitInfo?.signalCode ?? "none"}, ${sessions.length} session(s) torn down)`
          );
        }
        for (const sid of sessions) {
          const sessEntry = byId.get(sid);
          if (!sessEntry) continue;
          cancelPendingForSession(sid);
          try {
            sessEntry.events.publish({
              type: "session_died",
              data: {
                sessionId: sid,
                reason: "channel_closed",
                // BX9_P: thread exitCode/signalCode through.
                exitCode: exitInfo?.exitCode ?? null,
                signalCode: exitInfo?.signalCode ?? null
              }
            });
          } catch {
          }
          if (sessEntry.promptActive) {
            sessEntry.promptActive = false;
            activePromptCounter--;
            touchActivity();
          }
          byId.delete(sid);
          telemetry.metrics?.sessionLifecycle("die");
          info.client.markSessionClosed(sid);
          if (defaultEntry === sessEntry) defaultEntry = void 0;
          sessEntry.events.close();
        }
      });
      try {
        await telemetry.withSpan(
          "channel.initialize",
          {
            "qwen-code.daemon.bridge.operation": "channel.initialize"
          },
          async () => await withTimeout(
            connection.initialize({
              protocolVersion: PROTOCOL_VERSION,
              clientCapabilities: {
                fs: { readTextFile: true, writeTextFile: true }
              },
              clientInfo: { name: "qwen-serve-bridge", version: "0" }
            }),
            initTimeoutMs,
            "initialize"
          )
        );
      } catch (err) {
        info.isDying = true;
        await channel.kill().catch(() => {
        });
        throw err;
      }
      if (shuttingDown) {
        info.isDying = true;
        await channel.kill().catch(() => {
        });
        throw new Error("AcpSessionBridge is shutting down");
      }
      channelInfo = info;
      info.handshakeComplete = true;
      telemetry.metrics?.channelLifecycle("spawn");
      return info;
    })();
    inFlightChannelSpawn = promise;
    try {
      return await promise;
    } finally {
      inFlightChannelSpawn = void 0;
    }
  }
  __name(ensureChannel, "ensureChannel");
  async function doSpawn(modelServiceId, effectiveScope, requestedClientId) {
    const ci = await ensureChannel();
    let newSessionResp;
    try {
      newSessionResp = await telemetry.withSpan(
        "session.new",
        {
          "qwen-code.daemon.bridge.operation": "session.new",
          "qwen-code.daemon.session_scope": effectiveScope
        },
        async () => await withTimeout(
          ci.connection.newSession({
            cwd: boundWorkspace,
            mcpServers: []
          }),
          initTimeoutMs,
          "newSession"
        )
      );
    } catch (err) {
      if (ci.sessionIds.size === 0) {
        ci.isDying = true;
        await ci.channel.kill().catch(() => {
        });
      }
      throw err;
    }
    if (shuttingDown) {
      throw new Error("AcpSessionBridge is shutting down");
    }
    const entry = createSessionEntry(
      ci,
      newSessionResp.sessionId,
      boundWorkspace
    );
    seedSnapshotCaches(entry, newSessionResp);
    const clientId = registerClient(entry, requestedClientId);
    if (effectiveScope === "single" && !defaultEntry) defaultEntry = entry;
    if (modelServiceId) {
      await applyModelServiceId(
        entry,
        modelServiceId,
        initTimeoutMs,
        clientId
      ).catch(() => {
      });
    }
    if (!byId.has(entry.sessionId)) {
      throw new Error(
        `Session ${entry.sessionId} died during model-switch initialization`
      );
    }
    return {
      sessionId: entry.sessionId,
      workspaceCwd: entry.workspaceCwd,
      attached: false,
      clientId,
      createdAt: entry.createdAt
    };
  }
  __name(doSpawn, "doSpawn");
  async function applyModelServiceId(entry, modelId, timeoutMs, originatorClientId) {
    const conn = entry.connection;
    const transportClosed = getTransportClosedReject(entry);
    const work = entry.modelChangeQueue.then(async () => {
      entry.modelRoundtripInFlight = true;
      let succeeded = false;
      try {
        await Promise.race([
          withTimeout(
            conn.unstable_setSessionModel({
              sessionId: entry.sessionId,
              modelId
            }),
            timeoutMs,
            "setSessionModel"
          ),
          transportClosed
        ]);
        publishModelSwitched(entry, modelId, originatorClientId);
        broadcastWorkspaceEvent({
          type: "settings_changed",
          data: {
            key: "model.name",
            value: modelId
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
        succeeded = true;
      } catch (err) {
        entry.events.publish({
          type: "model_switch_failed",
          data: {
            sessionId: entry.sessionId,
            requestedModelId: modelId,
            error: err instanceof Error ? err.message : String(err)
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
        throw err;
      } finally {
        entry.modelRoundtripInFlight = false;
        if (succeeded) {
          void reconcileAfterRoundtrip(entry, "model");
        } else {
          writeStderrLine(
            `[reconcile] session=${entry.sessionId} target=model action=skipped reason=roundtrip_failed`
          );
        }
      }
    });
    entry.modelChangeQueue = work.then(
      () => void 0,
      () => void 0
    );
    return work;
  }
  __name(applyModelServiceId, "applyModelServiceId");
  const cancelPendingForSession = /* @__PURE__ */ __name((sessionId) => {
    permissionMediator.forgetSession(sessionId);
    byId.get(sessionId)?.pendingPermissionIds.clear();
  }, "cancelPendingForSession");
  const getTransportClosedReject = /* @__PURE__ */ __name((entry) => {
    if (!entry.transportClosedReject) {
      entry.transportClosedReject = entry.channel.exited.then(() => {
        throw new BridgeChannelClosedError(
          `mid-request (session ${entry.sessionId})`
        );
      });
    }
    return entry.transportClosedReject;
  }, "getTransportClosedReject");
  const resolveWorkspaceKey = /* @__PURE__ */ __name((workspaceCwd) => {
    if (!path2.isAbsolute(workspaceCwd)) {
      throw new Error(
        `workspaceCwd must be an absolute path; got "${workspaceCwd}"`
      );
    }
    const workspaceKey = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
    if (workspaceKey !== boundWorkspace) {
      throw new WorkspaceMismatchError(boundWorkspace, workspaceKey);
    }
    return workspaceKey;
  }, "resolveWorkspaceKey");
  const liveChannelInfo = /* @__PURE__ */ __name(() => {
    if (!channelInfo || channelInfo.isDying) return void 0;
    return channelInfo;
  }, "liveChannelInfo");
  const channelInfoForEntry = /* @__PURE__ */ __name((entry) => {
    if (channelInfo?.channel === entry.channel) return channelInfo;
    for (const info of aliveChannels) {
      if (info.channel === entry.channel) return info;
    }
    return void 0;
  }, "channelInfoForEntry");
  const assertLivePromptEntry = /* @__PURE__ */ __name((sessionId, entry) => {
    const info = channelInfoForEntry(entry);
    if (byId.get(sessionId) !== entry || !info || info.isDying) {
      throw new SessionNotFoundError(sessionId);
    }
  }, "assertLivePromptEntry");
  const getChannelClosedReject = /* @__PURE__ */ __name((info) => {
    if (!info.statusClosedReject) {
      info.statusClosedReject = info.channel.exited.then(() => {
        throw new BridgeChannelClosedError("mid-request (workspace status)");
      });
    }
    return info.statusClosedReject;
  }, "getChannelClosedReject");
  const requestWorkspaceStatus = /* @__PURE__ */ __name(async (method, idle, params = {}) => {
    const info = liveChannelInfo();
    if (!info) return idle();
    const response = await withTimeout(
      Promise.race([
        info.connection.extMethod(method, { ...params, cwd: boundWorkspace }),
        getChannelClosedReject(info)
      ]),
      initTimeoutMs,
      method
    );
    return response;
  }, "requestWorkspaceStatus");
  const requestSessionStatus = /* @__PURE__ */ __name(async (sessionId, method, params = {}) => {
    const entry = byId.get(sessionId);
    if (!entry) throw new SessionNotFoundError(sessionId);
    const info = channelInfoForEntry(entry);
    if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
    const response = await Promise.race([
      withTimeout(
        entry.connection.extMethod(method, { ...params, sessionId }),
        initTimeoutMs,
        method
      ),
      getTransportClosedReject(entry)
    ]);
    return response;
  }, "requestSessionStatus");
  const notifyAgentSessionClose = /* @__PURE__ */ __name(async (entry, ci, label) => {
    if (!ci || ci.channel !== entry.channel) return;
    try {
      await Promise.race([
        withTimeout(
          entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionClose, {
            sessionId: entry.sessionId
          }),
          initTimeoutMs,
          SERVE_CONTROL_EXT_METHODS.sessionClose
        ),
        getTransportClosedReject(entry)
      ]);
    } catch (err) {
      writeStderrLine(
        `qwen serve: ${label} ACP session close notification failed for session ${JSON.stringify(entry.sessionId)}: ${String(
          err instanceof Error ? err.message : err
        )}`
      );
    }
  }, "notifyAgentSessionClose");
  const broadcastWorkspaceEvent = /* @__PURE__ */ __name((envelope, skipSessionId) => {
    const sessions = Array.from(byId.values());
    let successCount = 0;
    let failureCount = 0;
    let skippedCount = 0;
    for (const entry of sessions) {
      if (skipSessionId !== void 0 && entry.sessionId === skipSessionId) {
        skippedCount += 1;
        continue;
      }
      try {
        const published = entry.events.publish(envelope);
        if (published === void 0) {
          failureCount += 1;
          teeServeDebugLine(
            `broadcastWorkspaceEvent: publish on session ${entry.sessionId} no-op (bus closed)`
          );
        } else {
          successCount += 1;
        }
      } catch (err) {
        failureCount += 1;
        const detail = `broadcastWorkspaceEvent: bus publish failed for session ${JSON.stringify(entry.sessionId)} (type=${envelope.type}): ${err instanceof Error ? err.message : String(err)}`;
        if (shuttingDown) {
          teeServeDebugLine(detail);
        } else {
          writeStderrLine(`qwen serve: ${detail}`);
        }
      }
    }
    const eligible = sessions.length - skippedCount;
    if (eligible > 0 && successCount === 0 && !shuttingDown) {
      writeStderrLine(
        `qwen serve: broadcastWorkspaceEvent type=${envelope.type} dropped on ALL ${failureCount} session bus(es); SSE subscribers will miss this event (GET fallback still authoritative)`
      );
    }
  }, "broadcastWorkspaceEvent");
  const createSessionEventBus = /* @__PURE__ */ __name(() => new EventBus(eventRingSize, void 0, new TurnBoundaryCompactionEngine()), "createSessionEventBus");
  const publishModelSwitched = /* @__PURE__ */ __name((entry, modelId, originatorClientId) => {
    entry.currentModelId = modelId;
    entry.modelPublishGeneration++;
    entry.events.publish({
      type: "model_switched",
      data: { sessionId: entry.sessionId, modelId },
      ...originatorClientId ? { originatorClientId } : {}
    });
  }, "publishModelSwitched");
  const publishApprovalModeChanged = /* @__PURE__ */ __name((entry, payload, originatorClientId) => {
    entry.currentApprovalMode = payload.next;
    entry.approvalModePublishGeneration++;
    entry.events.publish({
      type: "approval_mode_changed",
      data: {
        sessionId: entry.sessionId,
        previous: payload.previous,
        next: payload.next,
        persisted: payload.persisted
      },
      ...originatorClientId ? { originatorClientId } : {}
    });
  }, "publishApprovalModeChanged");
  const reconcileAfterRoundtrip = /* @__PURE__ */ __name(async (entry, target) => {
    const flagKey = target === "model" ? "modelReconciliationInFlight" : "approvalModeReconciliationInFlight";
    const genOf = /* @__PURE__ */ __name(() => target === "model" ? entry.modelPublishGeneration : entry.approvalModePublishGeneration, "genOf");
    if (entry[flagKey]) return;
    entry[flagKey] = true;
    const genBefore = genOf();
    let rerun = false;
    try {
      const status = await requestSessionStatus(
        entry.sessionId,
        SERVE_STATUS_EXT_METHODS.sessionContext
      );
      if (genOf() !== genBefore) {
        rerun = true;
        writeStderrLine(
          `[reconcile] session=${entry.sessionId} target=${target} action=skipped reason=generation_changed genBefore=${genBefore} genAfter=${genOf()}`
        );
        return;
      }
      if (target === "model") {
        const actual = status?.state?.models?.currentModelId;
        if (typeof actual === "string" && actual && actual !== entry.currentModelId) {
          writeStderrLine(
            `[reconcile] session=${entry.sessionId} target=model action=corrected cached=${entry.currentModelId ?? "<unset>"} actual=${actual}`
          );
          publishModelSwitched(entry, actual, void 0);
        }
      } else {
        const actual = status?.state?.modes?.currentModeId;
        if (actual && !KNOWN_APPROVAL_MODES.has(actual)) {
          writeStderrLine(
            `[reconcile] session=${entry.sessionId} target=approvalMode action=dropped reason=unknown_mode mode=${actual}`
          );
        } else if (actual && actual !== entry.currentApprovalMode) {
          writeStderrLine(
            `[reconcile] session=${entry.sessionId} target=approvalMode action=corrected cached=${entry.currentApprovalMode ?? "<unset>"} actual=${actual}`
          );
          publishApprovalModeChanged(
            entry,
            {
              previous: entry.currentApprovalMode ?? "default",
              next: actual,
              persisted: false
            },
            void 0
          );
        }
      }
    } catch (err) {
      writeStderrLine(
        `[reconcile] session=${entry.sessionId} target=${target} action=failed error=${err instanceof Error ? err.message : String(err)}`
      );
    } finally {
      entry[flagKey] = false;
      if (rerun) void reconcileAfterRoundtrip(entry, target);
    }
  }, "reconcileAfterRoundtrip");
  const createSessionEntry = /* @__PURE__ */ __name((ci, sessionId, workspaceCwd, events = createSessionEventBus()) => {
    const entry = {
      sessionId,
      workspaceCwd,
      createdAt: (/* @__PURE__ */ new Date()).toISOString(),
      channel: ci.channel,
      connection: ci.connection,
      events,
      promptQueue: Promise.resolve(),
      pendingPromptCount: 0,
      midTurnMessageQueue: [],
      modelChangeQueue: Promise.resolve(),
      approvalModeQueue: Promise.resolve(),
      modelPublishGeneration: 0,
      approvalModePublishGeneration: 0,
      pendingPermissionIds: /* @__PURE__ */ new Set(),
      clientIds: /* @__PURE__ */ new Map(),
      clientLastSeenAt: /* @__PURE__ */ new Map(),
      attachCount: 0,
      spawnOwnerWantedKill: false,
      promptActive: false,
      retryAllowed: false
    };
    ci.sessionIds.add(entry.sessionId);
    byId.set(entry.sessionId, entry);
    touchActivity();
    telemetry.metrics?.sessionLifecycle("spawn");
    ci.client.drainEarlyEvents(entry.sessionId, entry);
    return entry;
  }, "createSessionEntry");
  const seedSnapshotCaches = /* @__PURE__ */ __name((entry, resp) => {
    const model = resp.models?.currentModelId;
    if (typeof model === "string" && model.length > 0) {
      entry.currentModelId = model;
    } else if (model != null) {
      writeStderrLine(
        `[seed] session=${entry.sessionId} target=model action=dropped value=${JSON.stringify(model)} reason=invalid_type`
      );
    }
    const mode = resp.modes?.currentModeId;
    if (typeof mode === "string" && KNOWN_APPROVAL_MODES.has(mode)) {
      entry.currentApprovalMode = mode;
    } else if (mode != null) {
      writeStderrLine(
        `[seed] session=${entry.sessionId} target=approvalMode action=dropped value=${JSON.stringify(mode)} reason=${typeof mode !== "string" ? "invalid_type" : "unknown_mode"}`
      );
    }
  }, "seedSnapshotCaches");
  const isAcpSessionResourceNotFound = /* @__PURE__ */ __name((err, sessionId) => {
    if (!err || typeof err !== "object") return false;
    const maybe = err;
    if (maybe.code !== -32002) return false;
    const expectedUri = `session:${sessionId}`;
    if (maybe.data && typeof maybe.data === "object" && maybe.data.uri === expectedUri) {
      return true;
    }
    return typeof maybe.message === "string" && maybe.message === `Resource not found: ${expectedUri}`;
  }, "isAcpSessionResourceNotFound");
  const replayFieldsFor = /* @__PURE__ */ __name((entry, action) => {
    const snapshot = entry.events.snapshotReplay();
    if (!snapshot) return { lastEventId: entry.events.lastEventId };
    if (action === "load") {
      return {
        compactedReplay: snapshot.compactedTurns,
        liveJournal: snapshot.liveJournal,
        lastEventId: snapshot.lastEventId
      };
    }
    return { lastEventId: snapshot.lastEventId };
  }, "replayFieldsFor");
  async function restoreSession(action, req) {
    if (shuttingDown) {
      throw new Error("AcpSessionBridge is shutting down");
    }
    const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
    const existing = byId.get(req.sessionId);
    if (existing) {
      existing.attachCount++;
      const clientId = registerClient(existing, req.clientId);
      return {
        sessionId: existing.sessionId,
        workspaceCwd: existing.workspaceCwd,
        attached: true,
        clientId,
        createdAt: existing.createdAt,
        // Late attachers get the same ACP state the original restore
        // caller saw; spawn-only sessions don't carry a state payload.
        state: existing.restoreState ?? {},
        ...replayFieldsFor(existing, action)
      };
    }
    const inFlight = inFlightRestores.get(req.sessionId);
    if (inFlight) {
      if (action !== inFlight.action) {
        throw new RestoreInProgressError(
          req.sessionId,
          inFlight.action,
          action
        );
      }
      inFlight.coalesceState.count++;
      let restored;
      try {
        restored = await inFlight.promise;
      } catch (err) {
        inFlight.coalesceState.count--;
        throw err;
      }
      const entry = byId.get(restored.sessionId);
      if (!entry) {
        inFlight.coalesceState.count--;
        throw new SessionNotFoundError(
          restored.sessionId,
          "the agent child likely crashed during session restore \u2014 retry to restore the session"
        );
      }
      return {
        ...restored,
        attached: true,
        clientId: registerClient(entry, req.clientId),
        createdAt: entry.createdAt
      };
    }
    if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
      throw new SessionLimitExceededError(maxSessions);
    }
    const restoreEvents = createSessionEventBus();
    let registeredEntry;
    let ci;
    const coalesceState = { count: 0 };
    const promise = (async () => {
      pendingRestoreEvents.set(req.sessionId, restoreEvents);
      ci = await ensureChannel();
      ci.pendingRestoreIds.add(req.sessionId);
      ci.client.markRestoreInFlight(req.sessionId);
      const transportClosed = ci.channel.exited.then(() => {
        throw new BridgeChannelClosedError(`during session/${action}`);
      });
      transportClosed.catch(() => {
      });
      let state;
      try {
        if (action === "load") {
          state = await Promise.race([
            withTimeout(
              ci.connection.loadSession({
                sessionId: req.sessionId,
                cwd: workspaceKey,
                // Restore path drops per-request `mcpServers` (matches
                // `doSpawn`); daemon-wide MCP comes from settings on
                // the agent side. The SDK's `RestoreSessionRequest`
                // intentionally has no `mcpServers` field for the
                // same reason.
                mcpServers: []
              }),
              initTimeoutMs,
              "loadSession"
            ),
            transportClosed
          ]);
        } else {
          state = await Promise.race([
            withTimeout(
              ci.connection.unstable_resumeSession({
                sessionId: req.sessionId,
                cwd: workspaceKey,
                mcpServers: []
              }),
              initTimeoutMs,
              "resumeSession"
            ),
            transportClosed
          ]);
        }
      } catch (err) {
        restoreEvents.close();
        if (isAcpSessionResourceNotFound(err, req.sessionId)) {
          throw new SessionNotFoundError(req.sessionId);
        }
        if (ci.sessionIds.size === 0 && ci.pendingRestoreIds.size === 1 && ci.pendingRestoreIds.has(req.sessionId)) {
          ci.isDying = true;
          await ci.channel.kill().catch(() => {
          });
        }
        throw err;
      }
      if (shuttingDown) {
        restoreEvents.close();
        throw new Error("AcpSessionBridge is shutting down");
      }
      if (ci.isDying || !aliveChannels.has(ci)) {
        restoreEvents.close();
        throw new Error(
          `Session ${req.sessionId} restored on a closed agent channel`
        );
      }
      const racedEntry = byId.get(req.sessionId);
      if (racedEntry) {
        restoreEvents.close();
        racedEntry.attachCount += 1 + coalesceState.count;
        const clientId2 = registerClient(racedEntry, req.clientId);
        return {
          sessionId: racedEntry.sessionId,
          workspaceCwd: racedEntry.workspaceCwd,
          attached: true,
          clientId: clientId2,
          createdAt: racedEntry.createdAt,
          state: racedEntry.restoreState ?? {},
          ...replayFieldsFor(racedEntry, action)
        };
      }
      const entry = createSessionEntry(
        ci,
        req.sessionId,
        workspaceKey,
        restoreEvents
      );
      entry.restoreState = state;
      seedSnapshotCaches(entry, state);
      const clientId = registerClient(entry, req.clientId);
      entry.attachCount = coalesceState.count;
      registeredEntry = entry;
      return {
        sessionId: entry.sessionId,
        workspaceCwd: entry.workspaceCwd,
        attached: false,
        clientId,
        createdAt: entry.createdAt,
        state,
        ...replayFieldsFor(entry, action)
      };
    })().finally(() => {
      ci?.pendingRestoreIds.delete(req.sessionId);
      ci?.client.clearRestoreInFlight(req.sessionId);
      pendingRestoreEvents.delete(req.sessionId);
      if (!registeredEntry) {
        restoreEvents.close();
        ci?.client.markSessionClosed(req.sessionId);
      }
    });
    inFlightRestores.set(req.sessionId, { action, promise, coalesceState });
    try {
      return await promise;
    } finally {
      inFlightRestores.delete(req.sessionId);
    }
  }
  __name(restoreSession, "restoreSession");
  async function closeSessionImpl(sessionId, context, closeOpts) {
    const entry = byId.get(sessionId);
    if (!entry) throw new SessionNotFoundError(sessionId);
    let originatorClientId;
    if (context?.clientId !== void 0) {
      originatorClientId = resolveTrustedClientId(entry, context.clientId);
    }
    const reason = closeOpts?.reason ?? "client_close";
    writeStderrLine(
      `qwen serve: closing session ${JSON.stringify(sessionId)} (reason: ${reason})` + (originatorClientId ? ` by client ${JSON.stringify(originatorClientId)}` : "")
    );
    telemetry.event("session.close", {
      "qwen-code.daemon.bridge.operation": "session.close",
      "session.id": sessionId,
      "session.close.reason": reason
    });
    if (defaultEntry === entry) defaultEntry = void 0;
    const ci = channelInfoForEntry(entry);
    if (!ci) {
      writeStderrLine(
        `qwen serve: closeSession channelInfoForEntry returned undefined for session ${JSON.stringify(sessionId)} \u2014 channel cleanup skipped (entry's channel already torn down)`
      );
    }
    if (ci && ci.channel === entry.channel) {
      ci.sessionIds.delete(sessionId);
    }
    permissionMediator.forgetSession(sessionId);
    entry.pendingPermissionIds.clear();
    if (entry.promptActive) {
      entry.promptActive = false;
      activePromptCounter--;
      touchActivity();
    }
    byId.delete(sessionId);
    telemetry.metrics?.sessionLifecycle("close");
    ci?.client.markSessionClosed(sessionId);
    try {
      entry.events.publish({
        type: "session_closed",
        data: {
          sessionId,
          reason,
          // `data.closedBy` is kept for back-compat with existing
          // wire consumers; new code should read envelope-level
          // `originatorClientId` (matches `session_metadata_updated`,
          // `model_switched`, `approval_mode_changed`, etc.).
          ...originatorClientId ? { closedBy: originatorClientId } : {}
        },
        ...originatorClientId ? { originatorClientId } : {}
      });
    } catch {
    }
    entry.events.close();
    await notifyAgentSessionClose(entry, ci, "closeSession");
    try {
      await telemetry.withSpan(
        "session.close.cancel_active_prompt",
        {
          "qwen-code.daemon.bridge.operation": "session.close.cancel_active_prompt",
          "session.id": sessionId
        },
        async () => await entry.connection.cancel({ sessionId })
      );
    } catch {
    }
    if (ci && ci.sessionIds.size === 0 && ci.pendingRestoreIds.size === 0) {
      await startIdleTimer(ci, `closeSession "${sessionId}"`);
    }
  }
  __name(closeSessionImpl, "closeSessionImpl");
  startSessionReaper();
  return {
    getDaemonStatusSnapshot() {
      return {
        limits: {
          maxSessions: maxSessions === Infinity ? null : maxSessions,
          maxPendingPromptsPerSession: maxPendingPromptsPerSession === Infinity ? null : maxPendingPromptsPerSession,
          eventRingSize,
          channelIdleTimeoutMs: resolvedChannelIdleTimeoutMs(),
          sessionIdleTimeoutMs
        },
        sessionCount: byId.size,
        pendingPermissionCount: permissionMediator.pendingCount,
        channelLive: !!liveChannelInfo(),
        permissionPolicy: permissionMediator.policy,
        sessions: [...byId.values()].map((entry) => ({
          sessionId: entry.sessionId,
          workspaceCwd: entry.workspaceCwd,
          createdAt: entry.createdAt,
          ...entry.displayName ? { displayName: entry.displayName } : {},
          clientCount: entry.clientIds.size,
          subscriberCount: entry.events.subscriberCount,
          attachCount: entry.attachCount,
          pendingPromptCount: entry.pendingPromptCount,
          pendingPermissionCount: entry.pendingPermissionIds.size,
          hasActivePrompt: entry.promptActive,
          lastEventId: entry.events.lastEventId,
          ...entry.sessionLastSeenAt !== void 0 ? { lastSeenAt: entry.sessionLastSeenAt } : {},
          ...entry.currentModelId ? { currentModelId: entry.currentModelId } : {},
          ...entry.currentApprovalMode ? { currentApprovalMode: entry.currentApprovalMode } : {}
        }))
      };
    },
    get sessionCount() {
      return byId.size;
    },
    get activePromptCount() {
      return activePromptCounter;
    },
    get lastActivityAt() {
      return lastActivityTimestamp;
    },
    get idleSinceMs() {
      return lastActivityTimestamp !== null ? Date.now() - lastActivityTimestamp : null;
    },
    isChannelLive() {
      return !!liveChannelInfo();
    },
    get pendingPermissionCount() {
      return permissionMediator.pendingCount;
    },
    get permissionPolicy() {
      return permissionMediator.policy;
    },
    async loadSession(req) {
      return restoreSession("load", req);
    },
    async resumeSession(req) {
      return restoreSession("resume", req);
    },
    async spawnOrAttach(req) {
      if (shuttingDown) {
        throw new Error("AcpSessionBridge is shutting down");
      }
      const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
      if (req.sessionScope !== void 0 && req.sessionScope !== "single" && req.sessionScope !== "thread") {
        throw new InvalidSessionScopeError(req.sessionScope);
      }
      const effectiveScope = req.sessionScope ?? defaultSessionScope;
      if (effectiveScope === "single") {
        const existing = defaultEntry;
        if (existing) {
          existing.attachCount++;
          const clientId = registerClient(existing, req.clientId);
          if (req.modelServiceId) {
            await applyModelServiceId(
              existing,
              req.modelServiceId,
              initTimeoutMs,
              clientId
            ).catch(() => {
            });
          }
          return {
            sessionId: existing.sessionId,
            workspaceCwd: existing.workspaceCwd,
            attached: true,
            clientId,
            createdAt: existing.createdAt
          };
        }
        const inFlight = inFlightSpawns.get(workspaceKey);
        if (inFlight) {
          const session = await inFlight;
          const attachedEntry = byId.get(session.sessionId);
          if (attachedEntry) attachedEntry.attachCount++;
          if (!attachedEntry) {
            throw new SessionNotFoundError(
              session.sessionId,
              "the agent child likely crashed during initialization \u2014 retry to spawn a new session"
            );
          }
          const clientId = registerClient(attachedEntry, req.clientId);
          if (req.modelServiceId) {
            await applyModelServiceId(
              attachedEntry,
              req.modelServiceId,
              initTimeoutMs,
              clientId
            ).catch(() => {
            });
          }
          return { ...session, attached: true, clientId };
        }
      }
      if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
        throw new SessionLimitExceededError(maxSessions);
      }
      const promise = doSpawn(req.modelServiceId, effectiveScope, req.clientId);
      const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
      inFlightSpawns.set(tracker, promise);
      try {
        return await promise;
      } finally {
        inFlightSpawns.delete(tracker);
      }
    },
    // Keep this method non-async: admission failures must throw before
    // HTTP routes return 202.
    sendPrompt(sessionId, req, signal, context) {
      opts.onDiagnosticLine?.(
        `qwen serve: bridge sendPrompt for session=${sessionId}`,
        "info"
      );
      const capturedContext = telemetry.captureContext();
      const queuedAt = Date.now();
      const entry = byId.get(sessionId);
      if (!entry) return Promise.reject(new SessionNotFoundError(sessionId));
      const originatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      if (signal?.aborted) {
        throw new DOMException("Prompt aborted", "AbortError");
      }
      if (entry.pendingPromptCount >= maxPendingPromptsPerSession) {
        throw new PromptQueueFullError(
          maxPendingPromptsPerSession,
          entry.pendingPromptCount,
          sessionId
        );
      }
      entry.pendingPromptCount += 1;
      let promptSlotReleased = false;
      const releasePromptSlot = /* @__PURE__ */ __name(() => {
        if (promptSlotReleased) return;
        promptSlotReleased = true;
        entry.pendingPromptCount = Math.max(0, entry.pendingPromptCount - 1);
      }, "releasePromptSlot");
      const result = entry.promptQueue.then(
        () => telemetry.runWithContext(capturedContext, async () => {
          const queueWaitMs = Date.now() - queuedAt;
          telemetry.metrics?.promptQueueWait(queueWaitMs);
          const dispatchStartMs = Date.now();
          try {
            return await telemetry.withSpan(
              "prompt.dispatch",
              {
                "qwen-code.daemon.bridge.operation": "prompt.dispatch",
                "session.id": sessionId,
                "qwen-code.daemon.prompt.queue_wait_ms": queueWaitMs,
                ...context?.clientId ? { "qwen-code.client_id": context.clientId } : {}
              },
              async () => {
                const normalized = telemetry.injectPromptContext(
                  {
                    ...req,
                    sessionId
                  }
                );
                if (signal?.aborted) {
                  throw new DOMException("Prompt aborted", "AbortError");
                }
                assertLivePromptEntry(sessionId, entry);
                const requestedRetry = req.retry === true;
                const isRetry = requestedRetry && entry.retryAllowed;
                entry.retryAllowed = false;
                const promptRequest = (() => {
                  const copy = {
                    ...normalized
                  };
                  delete copy.retry;
                  const meta = copy._meta && typeof copy._meta === "object" ? { ...copy._meta } : {};
                  delete meta[DAEMON_RETRY_META_KEY];
                  if (isRetry) {
                    meta[DAEMON_RETRY_META_KEY] = true;
                  }
                  if (Object.keys(meta).length > 0) {
                    copy._meta = meta;
                  } else {
                    delete copy._meta;
                  }
                  return copy;
                })();
                entry.promptActive = true;
                activePromptCounter++;
                entry.sessionLastSeenAt = Date.now();
                touchActivity();
                if (originatorClientId === void 0) {
                  delete entry.activePromptOriginatorClientId;
                } else {
                  entry.activePromptOriginatorClientId = originatorClientId;
                }
                try {
                  entry.cancelBroadcast = false;
                  if (!isRetry) {
                    echoPromptToSessionBus(
                      entry,
                      promptRequest,
                      originatorClientId
                    );
                  }
                } catch (echoErr) {
                  delete entry.activePromptOriginatorClientId;
                  if (entry.promptActive) {
                    entry.promptActive = false;
                    activePromptCounter--;
                    touchActivity();
                  }
                  throw echoErr;
                }
                const promptPromise = entry.connection.prompt(promptRequest).finally(() => {
                  if (entry.promptActive) {
                    entry.promptActive = false;
                    activePromptCounter--;
                    entry.sessionLastSeenAt = Date.now();
                    touchActivity();
                  }
                  delete entry.activePromptOriginatorClientId;
                  if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && byId.has(sessionId)) {
                    void closeSessionImpl(sessionId, void 0, {
                      reason: "last_client_detached"
                    }).catch((err) => {
                      writeStderrLine(
                        `qwen serve: deferred close-on-prompt-complete failed for ${JSON.stringify(sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
                      );
                    });
                  }
                });
                const racedPromise = Promise.race([
                  promptPromise,
                  getTransportClosedReject(entry)
                ]);
                void racedPromise.then(
                  () => {
                  },
                  (err) => {
                    writeStderrLine(
                      `sendPrompt: forward failed for session ${sessionId}: ${extractErrorMessage(err)}`
                    );
                    broadcastPromptCancelledOnce(
                      entry,
                      sessionId,
                      originatorClientId,
                      "forward_failed"
                    );
                    cancelPendingForSession(sessionId);
                    entry.connection.cancel({ sessionId }).catch(() => {
                    });
                  }
                ).catch(() => {
                });
                if (!signal) return racedPromise;
                const onAbort = /* @__PURE__ */ __name(() => {
                  broadcastPromptCancelledOnce(
                    entry,
                    sessionId,
                    originatorClientId
                  );
                  cancelPendingForSession(sessionId);
                  entry.connection.cancel({ sessionId }).catch(() => {
                  });
                }, "onAbort");
                if (signal.aborted) {
                  onAbort();
                } else {
                  signal.addEventListener("abort", onAbort, { once: true });
                  if (signal.aborted) onAbort();
                  racedPromise.finally(() => signal.removeEventListener("abort", onAbort)).catch(() => {
                  });
                }
                return racedPromise;
              }
            );
          } finally {
            telemetry.metrics?.promptDuration(Date.now() - dispatchStartMs);
          }
        })
      );
      const promptId = context?.promptId;
      result.then(
        (promptResult) => {
          broadcastTurnComplete(
            entry,
            sessionId,
            promptResult,
            promptId,
            originatorClientId
          );
        },
        (err) => {
          if (err instanceof DOMException && err.name === "AbortError") return;
          broadcastTurnError(
            entry,
            sessionId,
            err,
            promptId,
            originatorClientId
          );
        }
      );
      entry.promptQueue = result.then(
        () => void 0,
        () => void 0
      );
      result.finally(() => {
        releasePromptSlot();
        if (entry.pendingPromptCount === 0 && entry.midTurnMessageQueue.length > 0) {
          writeStderrLine(
            `[mid-turn] session=${entry.sessionId} dropped ${entry.midTurnMessageQueue.length} undrained message(s) at idle; browser resends next turn`
          );
          entry.midTurnMessageQueue.length = 0;
        }
      }).catch(() => {
      });
      return result;
    },
    async cancelSession(sessionId, req, context) {
      opts.onDiagnosticLine?.(
        `qwen serve: bridge cancelSession for session=${sessionId}`,
        "info"
      );
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const cancelOriginatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      broadcastPromptCancelledOnce(entry, sessionId, cancelOriginatorClientId);
      cancelPendingForSession(sessionId);
      const notif = req ? { ...req, sessionId } : { sessionId };
      telemetry.metrics?.cancelled();
      await telemetry.withSpan(
        "session.cancel",
        {
          "qwen-code.daemon.bridge.operation": "session.cancel",
          "session.id": sessionId
        },
        async () => {
          try {
            await entry.connection.cancel(notif);
          } catch (err) {
            if (isNotCurrentlyGeneratingCancelError(err)) return;
            throw err;
          }
        }
      );
    },
    subscribeEvents(sessionId, subOpts) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const raw = entry.events.subscribe(subOpts);
      if (!subOpts?.snapshot) return raw;
      const snapshotFrame = /* @__PURE__ */ __name(() => ({
        v: EVENT_SCHEMA_VERSION,
        type: "session_snapshot",
        data: {
          sessionId: entry.sessionId,
          currentModelId: entry.currentModelId ?? null,
          currentApprovalMode: entry.currentApprovalMode ?? null
        }
      }), "snapshotFrame");
      async function* withSnapshot() {
        let injected = false;
        if (subOpts?.lastEventId === void 0) {
          yield snapshotFrame();
          injected = true;
        }
        for await (const event of raw) {
          yield event;
          if (!injected && event.type === "replay_complete") {
            yield snapshotFrame();
            injected = true;
          }
        }
      }
      __name(withSnapshot, "withSnapshot");
      return withSnapshot();
    },
    getSessionLastEventId(sessionId) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      return entry.events.lastEventId;
    },
    respondToPermission(requestId, response, context) {
      const sessionId = permissionMediator.peekSessionFor(requestId);
      if (sessionId === void 0 || !byId.has(sessionId)) {
        writeStderrLine(
          `qwen serve: legacy permission vote ${JSON.stringify(requestId)} has no live session (peek returned ${JSON.stringify(sessionId)}); returning 404.`
        );
        return false;
      }
      return this.respondToSessionPermission(
        sessionId,
        requestId,
        response,
        context
      );
    },
    respondToSessionPermission(sessionId, requestId, response, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const actualSessionId = permissionMediator.peekSessionFor(requestId);
      if (actualSessionId !== void 0 && actualSessionId !== sessionId) {
        teeServeDebugLine(
          `rejected permission vote ${JSON.stringify(requestId)} for session ${JSON.stringify(sessionId)}; request belongs to session ${JSON.stringify(actualSessionId)}.`
        );
        return false;
      }
      if (actualSessionId === void 0) {
        writeStderrLine(
          `qwen serve: rejected permission vote ${JSON.stringify(requestId)} for session ${JSON.stringify(sessionId)}; mediator has no pending or resolved record (unknown / timed out / LRU-evicted).`
        );
        return false;
      }
      const trustedClientId = resolveTrustedClientId(entry, context?.clientId);
      if (response.outcome.outcome === "selected" && response.outcome.optionId === CANCEL_VOTE_SENTINEL) {
        throw new InvalidPermissionOptionError(requestId, CANCEL_VOTE_SENTINEL);
      }
      const optionId = response.outcome.outcome === "selected" ? response.outcome.optionId : CANCEL_VOTE_SENTINEL;
      const voterMetadata = extractPermissionResponseMetadata(response);
      const outcome = permissionMediator.vote({
        requestId,
        sessionId,
        clientId: trustedClientId,
        optionId,
        receivedAtMs: Date.now(),
        fromLoopback: context?.fromLoopback ?? false,
        ...voterMetadata ? { metadata: voterMetadata } : {}
      });
      switch (outcome.kind) {
        case "resolved":
        case "recorded":
          return true;
        case "already_resolved":
          return false;
        case "unknown_request":
          teeServeDebugLine(
            `rejected permission vote ${JSON.stringify(requestId)} for session ${JSON.stringify(sessionId)}; mediator has no pending or resolved record.`
          );
          return false;
        case "forbidden":
          throw new PermissionForbiddenError(
            requestId,
            sessionId,
            outcome.reason
          );
        default: {
          const _exhaustive = outcome;
          throw new Error(
            `unreachable PermissionVoteOutcome: ${JSON.stringify(_exhaustive)}`
          );
        }
      }
    },
    async branchSession(sessionId, req, context) {
      if (shuttingDown) throw new Error("AcpSessionBridge is shutting down");
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      let originatorClientId;
      if (context?.clientId !== void 0) {
        originatorClientId = resolveTrustedClientId(entry, context.clientId);
      }
      const branchResult = entry.promptQueue.then(async () => {
        if (entry.promptActive) {
          throw new BranchWhilePromptActiveError(sessionId);
        }
        if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
          throw new SessionLimitExceededError(maxSessions);
        }
        const ci = await ensureChannel();
        const result = await withTimeout(
          ci.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionBranch, {
            sessionId,
            cwd: boundWorkspace,
            name: req.name
          }),
          initTimeoutMs,
          "branchSession"
        );
        if (!result || typeof result.newSessionId !== "string") {
          throw new Error(
            `branchSession: agent returned invalid response: ${JSON.stringify(result)}`
          );
        }
        const rawBranchName = result.displayName ?? result.title;
        const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
        let restored;
        try {
          restored = await restoreSession("load", {
            sessionId: result.newSessionId,
            workspaceCwd: boundWorkspace,
            clientId: context?.clientId
          });
        } catch (restoreErr) {
          writeStderrLine(
            `qwen serve: branchSession load failed for ${result.newSessionId}, attempting cleanup...`
          );
          try {
            await ci.connection.extMethod(
              SERVE_CONTROL_EXT_METHODS.sessionClose,
              { sessionId: result.newSessionId, cwd: boundWorkspace }
            );
          } catch (cleanupErr) {
            writeStderrLine(
              `qwen serve: branchSession cleanup of ${result.newSessionId} failed: ${cleanupErr instanceof Error ? cleanupErr.message : cleanupErr}`
            );
          }
          throw restoreErr;
        }
        const newEntry = byId.get(result.newSessionId);
        if (newEntry) newEntry.displayName = branchDisplayName;
        const eventData = {
          sourceSessionId: sessionId,
          newSessionId: result.newSessionId,
          displayName: branchDisplayName
        };
        const branchEnvelope = {
          type: "session_branched",
          data: eventData,
          ...originatorClientId ? { originatorClientId } : {}
        };
        newEntry?.events.publish(branchEnvelope);
        return {
          ...restored,
          displayName: branchDisplayName,
          forkedFrom: {
            sessionId,
            displayName: entry.displayName ?? sessionId.slice(0, 8)
          }
        };
      });
      entry.promptQueue = branchResult.then(
        () => void 0,
        () => void 0
      );
      return branchResult;
    },
    async closeSession(sessionId, context, closeOpts) {
      return closeSessionImpl(sessionId, context, closeOpts);
    },
    updateSessionMetadata(sessionId, metadata, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const metadataOriginatorClientId = context?.clientId !== void 0 ? resolveTrustedClientId(entry, context.clientId) : void 0;
      if (metadata.displayName !== void 0) {
        if (typeof metadata.displayName !== "string" || metadata.displayName.length > MAX_DISPLAY_NAME_LENGTH) {
          throw new InvalidSessionMetadataError(
            "displayName",
            `must be a string of at most ${MAX_DISPLAY_NAME_LENGTH} characters`
          );
        }
        if (hasControlCharacter(metadata.displayName)) {
          throw new InvalidSessionMetadataError(
            "displayName",
            "must not contain control characters"
          );
        }
        const nextDisplayName = metadata.displayName || void 0;
        if (entry.displayName !== nextDisplayName) {
          entry.displayName = nextDisplayName;
          writeStderrLine(
            `qwen serve: updated session metadata ${JSON.stringify(sessionId)} displayName=${entry.displayName === void 0 ? "cleared" : "set"}` + (context?.clientId ? ` by client ${JSON.stringify(context.clientId)}` : "")
          );
          if (nextDisplayName) {
            entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionTitle, {
              sessionId,
              displayName: nextDisplayName,
              titleSource: "manual"
            }).then((res) => {
              const r = res;
              if (r && r.persisted === false) {
                writeStderrLine(
                  `qwen serve: displayName for ${sessionId} was not persisted (recording service unavailable)`
                );
              }
            }).catch((err) => {
              writeStderrLine(
                `qwen serve: failed to persist displayName for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`
              );
            });
          }
          try {
            entry.events.publish({
              type: "session_metadata_updated",
              data: { sessionId, displayName: entry.displayName },
              ...metadataOriginatorClientId ? { originatorClientId: metadataOriginatorClientId } : {}
            });
          } catch {
          }
        }
      }
      return { displayName: entry.displayName };
    },
    listWorkspaceSessions(workspaceCwd) {
      if (!path2.isAbsolute(workspaceCwd)) return [];
      const key = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
      if (key !== boundWorkspace) return [];
      const out = [];
      for (const entry of byId.values()) {
        if (entry.workspaceCwd === key) {
          out.push({
            sessionId: entry.sessionId,
            workspaceCwd: entry.workspaceCwd,
            createdAt: entry.createdAt,
            displayName: entry.displayName,
            clientCount: entry.clientIds.size,
            hasActivePrompt: entry.promptActive
          });
        }
      }
      return out;
    },
    recordHeartbeat(sessionId, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const clientId = resolveTrustedClientId(entry, context?.clientId);
      const lastSeenAt = Date.now();
      entry.sessionLastSeenAt = lastSeenAt;
      if (clientId !== void 0) {
        entry.clientLastSeenAt.set(clientId, lastSeenAt);
      }
      return {
        sessionId: entry.sessionId,
        ...clientId !== void 0 ? { clientId } : {},
        lastSeenAt
      };
    },
    getHeartbeatState(sessionId) {
      const entry = byId.get(sessionId);
      if (!entry) return void 0;
      return {
        ...entry.sessionLastSeenAt !== void 0 ? { sessionLastSeenAt: entry.sessionLastSeenAt } : {},
        clientLastSeenAt: new Map(entry.clientLastSeenAt)
      };
    },
    publishWorkspaceEvent(event) {
      const sessions = Array.from(byId.values());
      let successCount = 0;
      let failureCount = 0;
      for (const entry of sessions) {
        try {
          const published = entry.events.publish(event);
          if (published === void 0) {
            failureCount += 1;
            teeServeDebugLine(
              `publishWorkspaceEvent: publish on session ${entry.sessionId} no-op (bus closed)`
            );
          } else {
            successCount += 1;
          }
        } catch (err) {
          failureCount += 1;
          const detail = `publishWorkspaceEvent: bus publish failed for session ${JSON.stringify(entry.sessionId)} (type=${event.type}): ${err instanceof Error ? err.message : String(err)}`;
          if (shuttingDown) {
            teeServeDebugLine(detail);
          } else {
            writeStderrLine(`qwen serve: ${detail}`);
          }
        }
      }
      if (sessions.length > 0 && successCount === 0 && !shuttingDown) {
        writeStderrLine(
          `qwen serve: publishWorkspaceEvent type=${event.type} dropped on ALL ${failureCount} session bus(es); SSE subscribers will miss this event (GET fallback still authoritative)`
        );
      }
    },
    knownClientIds() {
      const out = /* @__PURE__ */ new Set();
      for (const entry of byId.values()) {
        for (const id of entry.clientIds.keys()) out.add(id);
      }
      return out;
    },
    async queryWorkspaceStatus(method, idle) {
      return requestWorkspaceStatus(method, idle);
    },
    async invokeWorkspaceCommand(method, params, invokeOpts) {
      const info = liveChannelInfo();
      if (!info) throw new SessionNotFoundError(`workspace-command:${method}`);
      const timeout = invokeOpts?.timeoutMs ?? initTimeoutMs;
      const response = await withTimeout(
        Promise.race([
          info.connection.extMethod(method, params ?? {}),
          getChannelClosedReject(info)
        ]),
        timeout,
        method
      );
      return response;
    },
    async getWorkspaceMcpToolsStatus(serverName) {
      return requestWorkspaceStatus(
        SERVE_STATUS_EXT_METHODS.workspaceMcpTools,
        () => ({
          v: STATUS_SCHEMA_VERSION,
          workspaceCwd: boundWorkspace,
          serverName,
          initialized: false,
          acpChannelLive: false,
          tools: [],
          errors: [
            {
              kind: "mcp_tools",
              status: "not_started",
              hint: "spawn a session to populate"
            }
          ]
        }),
        { serverName }
      );
    },
    async getWorkspaceToolsStatus() {
      return requestWorkspaceStatus(
        SERVE_STATUS_EXT_METHODS.workspaceTools,
        () => ({
          v: STATUS_SCHEMA_VERSION,
          workspaceCwd: boundWorkspace,
          initialized: true,
          acpChannelLive: false,
          tools: [],
          errors: [
            {
              kind: "tools",
              status: "not_started",
              hint: "spawn a session to populate"
            }
          ]
        })
      );
    },
    async getSessionContextStatus(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionContext
      );
    },
    async getSessionContextUsageStatus(sessionId, opts2) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionContextUsage,
        { detail: opts2?.detail === true }
      );
    },
    async getSessionSupportedCommandsStatus(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionSupportedCommands
      );
    },
    async getSessionTasksStatus(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionTasks
      );
    },
    async getSessionLspStatus(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionLspStatus
      );
    },
    async cancelSessionTask(sessionId, taskId, taskKind) {
      return requestSessionStatus(
        sessionId,
        SERVE_CONTROL_EXT_METHODS.sessionTaskCancel,
        { taskId, taskKind }
      );
    },
    async clearSessionGoal(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_CONTROL_EXT_METHODS.sessionGoalClear
      );
    },
    async getSessionStatsStatus(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionStats
      );
    },
    async getWorkspaceHooksStatus() {
      return requestWorkspaceStatus(
        SERVE_STATUS_EXT_METHODS.workspaceHooks,
        () => createIdleWorkspaceHooksStatus(boundWorkspace)
      );
    },
    async getSessionHooksStatus(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionHooks
      );
    },
    async getWorkspaceExtensionsStatus() {
      return requestWorkspaceStatus(
        SERVE_STATUS_EXT_METHODS.workspaceExtensions,
        () => createIdleWorkspaceExtensionsStatus(boundWorkspace)
      );
    },
    async refreshExtensionsForAllSessions(data) {
      const sessions = Array.from(byId.values());
      const results = await Promise.all(
        sessions.map(async (entry) => {
          const info = channelInfoForEntry(entry);
          if (!info || info.isDying) {
            return { refreshed: 0, failed: 0 };
          }
          try {
            await Promise.race([
              withTimeout(
                entry.connection.extMethod(
                  SERVE_CONTROL_EXT_METHODS.workspaceExtensionsRefresh,
                  { sessionId: entry.sessionId }
                ),
                3e4,
                SERVE_CONTROL_EXT_METHODS.workspaceExtensionsRefresh
              ),
              getTransportClosedReject(entry)
            ]);
            return { refreshed: 1, failed: 0 };
          } catch (err) {
            writeServeDebugLine(
              `refreshExtensions: session ${entry.sessionId} failed: ${err instanceof Error ? err.message : String(err)}`
            );
            return { refreshed: 0, failed: 1 };
          }
        })
      );
      const refreshed = results.reduce(
        (sum, result) => sum + result.refreshed,
        0
      );
      const failed = results.reduce((sum, result) => sum + result.failed, 0);
      if (refreshed > 0 || failed > 0 || data?.status !== void 0) {
        broadcastWorkspaceEvent({
          type: "extensions_changed",
          data: { ...data, refreshed, failed }
        });
      }
      return { refreshed, failed };
    },
    broadcastExtensionsChanged(data) {
      broadcastWorkspaceEvent({
        type: "extensions_changed",
        data
      });
    },
    async setSessionModel(sessionId, req, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const originatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      const normalized = { ...req, sessionId };
      const conn = entry.connection;
      const transportClosed = getTransportClosedReject(entry);
      const work = entry.modelChangeQueue.then(async () => {
        entry.modelRoundtripInFlight = true;
        let succeeded = false;
        try {
          const result = await Promise.race([
            withTimeout(
              conn.unstable_setSessionModel(normalized),
              initTimeoutMs,
              "setSessionModel"
            ),
            transportClosed
          ]);
          publishModelSwitched(entry, req.modelId, originatorClientId);
          broadcastWorkspaceEvent({
            type: "settings_changed",
            data: {
              key: "model.name",
              value: req.modelId
            },
            ...originatorClientId ? { originatorClientId } : {}
          });
          succeeded = true;
          return result;
        } finally {
          entry.modelRoundtripInFlight = false;
          if (succeeded) {
            void reconcileAfterRoundtrip(entry, "model");
          } else {
            writeStderrLine(
              `[reconcile] session=${entry.sessionId} target=model action=skipped reason=roundtrip_failed`
            );
          }
        }
      });
      entry.modelChangeQueue = work.then(
        () => void 0,
        () => void 0
      );
      let response;
      try {
        response = await work;
      } catch (err) {
        entry.events.publish({
          type: "model_switch_failed",
          data: {
            sessionId: entry.sessionId,
            requestedModelId: req.modelId,
            error: err instanceof Error ? err.message : String(err)
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
        throw err;
      }
      return response;
    },
    async setSessionLanguage(sessionId, params, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const info = channelInfoForEntry(entry);
      if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
      const originatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      const result = await Promise.race([
        withTimeout(
          entry.connection.extMethod(
            SERVE_CONTROL_EXT_METHODS.sessionLanguage,
            {
              sessionId,
              language: params.language,
              syncOutputLanguage: params.syncOutputLanguage
            }
          ),
          initTimeoutMs,
          SERVE_CONTROL_EXT_METHODS.sessionLanguage
        ),
        getTransportClosedReject(entry)
      ]);
      try {
        entry.events.publish({
          type: "language_changed",
          data: {
            sessionId: entry.sessionId,
            language: result.language,
            outputLanguage: result.outputLanguage ?? null,
            refreshed: result.refreshed ?? false
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
      } catch (err) {
        writeServeDebugLine(
          `language_changed event publish failed: ${err instanceof Error ? err.message : String(err)}`
        );
      }
      return {
        language: result.language,
        outputLanguage: result.outputLanguage ?? null,
        refreshed: result.refreshed ?? false
      };
    },
    async setSessionApprovalMode(sessionId, mode, opts2, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const info = channelInfoForEntry(entry);
      if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
      const originatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      if (opts2.persist && !persistApprovalMode) {
        throw new Error(
          "setSessionApprovalMode called with `persist: true` but no `persistApprovalMode` callback wired in BridgeOptions. runQwenServe wires the production callback; direct embeds and tests must opt in or omit `persist`."
        );
      }
      const approvalWork = entry.approvalModeQueue.then(async () => {
        entry.approvalModeRoundtripInFlight = true;
        let succeeded = false;
        try {
          const response = await Promise.race([
            withTimeout(
              entry.connection.extMethod(
                SERVE_CONTROL_EXT_METHODS.sessionApprovalMode,
                { sessionId, mode }
              ),
              initTimeoutMs,
              SERVE_CONTROL_EXT_METHODS.sessionApprovalMode
            ),
            getTransportClosedReject(entry)
          ]);
          if (typeof response.current !== "string" || !KNOWN_APPROVAL_MODES.has(response.current)) {
            throw new Error(
              `Agent returned unknown approval mode: ${JSON.stringify(response.current)}`
            );
          }
          let persisted = false;
          if (opts2.persist) {
            try {
              await withTimeout(
                persistApprovalMode?.(boundWorkspace, mode) ?? Promise.resolve(),
                PERSIST_TIMEOUT_MS,
                "persistApprovalMode"
              );
              persisted = persistApprovalMode !== void 0;
            } catch (err) {
              writeStderrLine(
                `setSessionApprovalMode: persist failed: ${err instanceof Error ? err.message : String(err)}`
              );
            }
          }
          publishApprovalModeChanged(
            entry,
            {
              previous: response.previous,
              next: response.current,
              persisted
            },
            originatorClientId
          );
          if (persisted) {
            broadcastWorkspaceEvent(
              {
                type: "approval_mode_changed",
                data: {
                  sessionId: entry.sessionId,
                  previous: response.previous,
                  next: response.current,
                  persisted
                },
                ...originatorClientId ? { originatorClientId } : {}
              },
              entry.sessionId
            );
            for (const peer of byId.values()) {
              if (peer.sessionId === entry.sessionId) {
                continue;
              }
              peer.currentApprovalMode = response.current;
            }
          }
          succeeded = true;
          return {
            sessionId: entry.sessionId,
            mode: response.current,
            previous: response.previous,
            persisted
          };
        } finally {
          entry.approvalModeRoundtripInFlight = false;
          if (succeeded) {
            void reconcileAfterRoundtrip(entry, "approvalMode");
          } else {
            writeStderrLine(
              `[reconcile] session=${entry.sessionId} target=approvalMode action=skipped reason=roundtrip_failed`
            );
          }
        }
      });
      entry.approvalModeQueue = approvalWork.then(
        () => void 0,
        () => void 0
      );
      try {
        return await approvalWork;
      } catch (err) {
        const data = err?.data;
        if (data && typeof data === "object" && "errorKind" in data && data.errorKind === "trust_gate") {
          const rawMessage = err?.message;
          const message = typeof rawMessage === "string" ? rawMessage : "Trust-gate rejection from ACP child";
          throw new TrustGateError(message);
        }
        throw err;
      }
    },
    async generateSessionRecap(sessionId, _context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const info = channelInfoForEntry(entry);
      if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
      opts.onDiagnosticLine?.(
        `qwen serve: bridge generateSessionRecap dispatching ext-method for session=${sessionId}`,
        "info"
      );
      const response = await Promise.race([
        withTimeout(
          entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionRecap, {
            sessionId
          }),
          SESSION_RECAP_TIMEOUT_MS,
          SERVE_CONTROL_EXT_METHODS.sessionRecap
        ),
        getTransportClosedReject(entry)
      ]);
      opts.onDiagnosticLine?.(
        `qwen serve: bridge generateSessionRecap completed for session=${sessionId} recap=${response.recap ? `len=${response.recap.length}` : "null"}`,
        "info"
      );
      return {
        sessionId: entry.sessionId,
        recap: response.recap ?? null
      };
    },
    enqueueMidTurnMessage(sessionId, message, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const originatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      const trimmed = message.trim();
      if (trimmed.length === 0 || entry.pendingPromptCount === 0) {
        writeStderrLine(
          `[mid-turn] session=${entry.sessionId} rejected: ${trimmed.length === 0 ? "empty message" : "session idle"}; browser keeps it for next turn`
        );
        return { accepted: false };
      }
      if (entry.midTurnMessageQueue.length >= MAX_MID_TURN_QUEUE_DEPTH) {
        writeStderrLine(
          `[mid-turn] session=${entry.sessionId} rejected: queue full (depth ${entry.midTurnMessageQueue.length} >= ${MAX_MID_TURN_QUEUE_DEPTH}); browser keeps it for next turn`
        );
        return { accepted: false };
      }
      entry.midTurnMessageQueue.push({ text: trimmed, originatorClientId });
      return { accepted: true };
    },
    async generateSessionBtw(sessionId, question, signal, _context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const info = channelInfoForEntry(entry);
      if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
      if (signal?.aborted) return { sessionId, answer: null };
      const races = [
        withTimeout(
          entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionBtw, {
            sessionId,
            question
          }),
          SESSION_BTW_TIMEOUT_MS,
          SERVE_CONTROL_EXT_METHODS.sessionBtw
        ),
        getTransportClosedReject(entry)
      ];
      let cleanupAbort;
      if (signal) {
        races.push(
          new Promise((_, reject) => {
            const handler = /* @__PURE__ */ __name(() => reject(new DOMException("Aborted", "AbortError")), "handler");
            signal.addEventListener("abort", handler, { once: true });
            cleanupAbort = /* @__PURE__ */ __name(() => signal.removeEventListener("abort", handler), "cleanupAbort");
          })
        );
      }
      let response;
      try {
        response = await Promise.race(races);
      } finally {
        cleanupAbort?.();
      }
      return {
        sessionId: entry.sessionId,
        answer: response.answer ?? null
      };
    },
    async launchSessionForkAgent(sessionId, directive, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const info = channelInfoForEntry(entry);
      if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
      resolveTrustedClientId(entry, context?.clientId);
      const trimmed = directive.trim();
      if (!trimmed) {
        throw new Error("Fork directive is required");
      }
      if (entry.pendingPromptCount > 0 || entry.promptActive) {
        throw new SessionBusyError(
          sessionId,
          "Cannot fork while a response or tool call is in progress"
        );
      }
      return entry.promptQueue.then(async () => {
        if (entry.pendingPromptCount > 0 || entry.promptActive) {
          throw new SessionBusyError(
            sessionId,
            "Cannot fork while a response or tool call is in progress"
          );
        }
        opts.onDiagnosticLine?.(
          `qwen serve: launchSessionForkAgent requested for session=${sessionId}`,
          "info"
        );
        let response;
        try {
          response = await Promise.race([
            withTimeout(
              entry.connection.extMethod(
                SERVE_CONTROL_EXT_METHODS.sessionForkAgent,
                {
                  sessionId,
                  directive: trimmed
                }
              ),
              initTimeoutMs,
              SERVE_CONTROL_EXT_METHODS.sessionForkAgent
            ),
            getTransportClosedReject(entry)
          ]);
        } catch (error) {
          opts.onDiagnosticLine?.(
            `qwen serve: launchSessionForkAgent failed for session=${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
            "warn"
          );
          throw error;
        }
        const result = {
          sessionId: entry.sessionId,
          description: response.description ?? trimmed.slice(0, 60),
          launched: response.launched === true
        };
        opts.onDiagnosticLine?.(
          `qwen serve: launchSessionForkAgent completed for session=${sessionId} launched=${result.launched}`,
          "info"
        );
        return result;
      });
    },
    async executeShellCommand(sessionId, command, signal, context) {
      opts.onDiagnosticLine?.(
        `qwen serve: bridge executeShellCommand for session=${sessionId}`,
        "info"
      );
      if (opts.sessionShellCommandEnabled !== true) {
        throw new SessionShellDisabledError();
      }
      if (context?.clientId === void 0) {
        throw new SessionShellClientRequiredError();
      }
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const originatorClientId = resolveTrustedClientId(
        entry,
        context.clientId
      );
      if (signal?.aborted) {
        return { exitCode: null, output: "", aborted: true };
      }
      const cwd = entry.workspaceCwd;
      entry.events.publish({
        type: "user_shell_command",
        data: { sessionId, command, cwd },
        ...originatorClientId ? { originatorClientId } : {}
      });
      const outputChunks = [];
      const abort = new AbortController();
      const onSignalAbort = /* @__PURE__ */ __name(() => abort.abort(), "onSignalAbort");
      signal?.addEventListener("abort", onSignalAbort, { once: true });
      try {
        const handle = await ShellExecutionService.execute(
          command,
          cwd,
          (event) => {
            if (event.type === "data") {
              const chunk = typeof event.chunk === "string" ? event.chunk : event.chunk.map(
                (line) => line.map((t) => t.text).join("")
              ).join("\n");
              outputChunks.push(chunk);
              entry.events.publish({
                type: "session_update",
                data: {
                  sessionId,
                  update: {
                    sessionUpdate: "shell_output",
                    output: chunk,
                    _meta: {
                      serverTimestamp: Date.now(),
                      source: "user-shell"
                    }
                  }
                },
                ...originatorClientId ? { originatorClientId } : {}
              });
            }
          },
          abort.signal,
          false,
          { terminalWidth: 120, terminalHeight: 40 },
          { streamStdout: true }
        );
        const timeoutId = setTimeout(
          () => abort.abort(),
          SHELL_COMMAND_TIMEOUT_MS
        );
        timeoutId.unref();
        const result = await handle.result;
        clearTimeout(timeoutId);
        const exitCode = result.exitCode;
        const aborted = result.aborted;
        const output = outputChunks.join("") || result.output;
        entry.events.publish({
          type: "user_shell_result",
          data: {
            sessionId,
            exitCode,
            signal: result.signal,
            aborted,
            _meta: { serverTimestamp: Date.now() }
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
        const historyOutput = output.length > MAX_SHELL_OUTPUT_FOR_HISTORY ? output.substring(0, MAX_SHELL_OUTPUT_FOR_HISTORY) + "\n... (truncated)" : output;
        try {
          await entry.connection.extMethod(
            SERVE_CONTROL_EXT_METHODS.sessionShellHistory,
            { sessionId, command, output: historyOutput, exitCode }
          );
        } catch (err) {
          writeServeDebugLine(
            `shell history injection failed for session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`
          );
        }
        return { exitCode, output, aborted };
      } catch (err) {
        entry.events.publish({
          type: "user_shell_result",
          data: {
            sessionId,
            exitCode: null,
            signal: null,
            aborted: false,
            error: err instanceof Error ? err.message : String(err),
            _meta: { serverTimestamp: Date.now() }
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
        throw err;
      } finally {
        signal?.removeEventListener("abort", onSignalAbort);
      }
    },
    async getRewindSnapshots(sessionId) {
      return requestSessionStatus(
        sessionId,
        SERVE_STATUS_EXT_METHODS.sessionRewindSnapshots
      );
    },
    async rewindSession(sessionId, req, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const info = channelInfoForEntry(entry);
      if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
      const originatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      let response;
      try {
        response = await Promise.race([
          withTimeout(
            entry.connection.extMethod(
              SERVE_CONTROL_EXT_METHODS.sessionRewind,
              {
                sessionId,
                promptId: req.promptId,
                rewindFiles: req.rewindFiles !== false
              }
            ),
            initTimeoutMs,
            SERVE_CONTROL_EXT_METHODS.sessionRewind
          ),
          getTransportClosedReject(entry)
        ]);
      } catch (err) {
        const data = err?.data;
        if (data && typeof data === "object" && "errorKind" in data) {
          const kind = data.errorKind;
          const msg = err?.message ?? "Rewind failed";
          if (kind === "session_busy") {
            throw new SessionBusyError(sessionId, msg);
          }
          if (kind === "invalid_rewind_target") {
            throw new InvalidRewindTargetError(sessionId, msg);
          }
        }
        throw err;
      }
      const targetTurnIndex = response["targetTurnIndex"] ?? 0;
      const filesChanged = response["filesChanged"] ?? [];
      const filesFailed = response["filesFailed"] ?? [];
      try {
        entry.events.publish({
          type: "session_rewound",
          data: {
            sessionId,
            promptId: req.promptId,
            targetTurnIndex,
            filesChanged,
            filesFailed
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
      } catch {
      }
      return {
        rewound: filesFailed.length === 0,
        targetTurnIndex,
        filesChanged,
        filesFailed
      };
    },
    async manageMcpServer(serverName, action, originatorClientId) {
      const info = liveChannelInfo();
      if (!info) {
        throw new SessionNotFoundError(`mcp:${serverName}`);
      }
      const timeout = action === "authenticate" ? MCP_OAUTH_TIMEOUT_MS : MCP_RESTART_TIMEOUT_MS;
      const response = await Promise.race([
        withTimeout(
          info.connection.extMethod(
            SERVE_CONTROL_EXT_METHODS.workspaceMcpManage,
            { serverName, action, originatorClientId }
          ),
          timeout,
          SERVE_CONTROL_EXT_METHODS.workspaceMcpManage
        ),
        getChannelClosedReject(info)
      ]);
      broadcastWorkspaceEvent({
        type: "mcp_server_changed",
        data: {
          serverName: response.serverName,
          action: response.action,
          originatorClientId
        },
        ...originatorClientId ? { originatorClientId } : {}
      });
      return response;
    },
    async generateWorkspaceAgent(description, _originatorClientId) {
      const info = liveChannelInfo();
      if (!info) {
        throw new SessionNotFoundError("agents:generate");
      }
      return await Promise.race([
        withTimeout(
          info.connection.extMethod(
            SERVE_CONTROL_EXT_METHODS.workspaceAgentGenerate,
            { description }
          ),
          MCP_RESTART_TIMEOUT_MS,
          SERVE_CONTROL_EXT_METHODS.workspaceAgentGenerate
        ),
        getChannelClosedReject(info)
      ]);
    },
    async addRuntimeMcpServer(name, config, originatorClientId) {
      const info = liveChannelInfo();
      if (!info) {
        throw Object.assign(
          new Error(`No live ACP channel for runtime MCP add: ${name}`),
          { data: { errorKind: "acp_channel_unavailable" } }
        );
      }
      const response = await Promise.race([
        withTimeout(
          info.connection.extMethod(
            SERVE_CONTROL_EXT_METHODS.workspaceMcpRuntimeAdd,
            { name, config, originatorClientId }
          ),
          MCP_RESTART_SERVER_DEADLINE_MS,
          SERVE_CONTROL_EXT_METHODS.workspaceMcpRuntimeAdd
        ),
        getChannelClosedReject(info)
      ]);
      const addSkipped = response.skipped === true;
      if (!addSkipped) {
        const ok = response;
        broadcastWorkspaceEvent({
          type: "mcp_server_added",
          data: {
            name: ok.name,
            transport: ok.transport,
            replaced: ok.replaced,
            shadowedSettings: ok.shadowedSettings,
            toolCount: ok.toolCount,
            originatorClientId: ok.originatorClientId
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
      }
      return response;
    },
    async removeRuntimeMcpServer(name, originatorClientId) {
      const info = liveChannelInfo();
      if (!info) {
        throw Object.assign(
          new Error(`No live ACP channel for runtime MCP remove: ${name}`),
          { data: { errorKind: "acp_channel_unavailable" } }
        );
      }
      const response = await Promise.race([
        withTimeout(
          info.connection.extMethod(
            SERVE_CONTROL_EXT_METHODS.workspaceMcpRuntimeRemove,
            { name, originatorClientId }
          ),
          MCP_RESTART_SERVER_DEADLINE_MS,
          SERVE_CONTROL_EXT_METHODS.workspaceMcpRuntimeRemove
        ),
        getChannelClosedReject(info)
      ]);
      const removeSkipped = response.skipped === true;
      if (!removeSkipped) {
        const ok = response;
        broadcastWorkspaceEvent({
          type: "mcp_server_removed",
          data: {
            name: ok.name,
            wasShadowingSettings: ok.wasShadowingSettings,
            originatorClientId: ok.originatorClientId
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
      }
      return response;
    },
    async killSession(sessionId, opts2) {
      const entry = byId.get(sessionId);
      if (!entry) return;
      if (opts2?.requireZeroAttaches && entry.attachCount > 0) {
        entry.spawnOwnerWantedKill = true;
        return;
      }
      permissionMediator.forgetSession(sessionId);
      entry.pendingPermissionIds.clear();
      if (entry.promptActive) {
        entry.promptActive = false;
        activePromptCounter--;
        touchActivity();
      }
      if (defaultEntry === entry) defaultEntry = void 0;
      byId.delete(sessionId);
      telemetry.metrics?.sessionLifecycle("die");
      const ci = channelInfoForEntry(entry);
      if (!ci) {
        writeStderrLine(
          `qwen serve: killSession channelInfoForEntry returned undefined for session ${JSON.stringify(sessionId)} \u2014 channel cleanup skipped (entry's channel already torn down)`
        );
      }
      if (ci && ci.channel === entry.channel) {
        ci.sessionIds.delete(sessionId);
      }
      await notifyAgentSessionClose(entry, ci, "killSession");
      ci?.client.markSessionClosed(sessionId);
      try {
        entry.events.publish({
          type: "session_died",
          data: { sessionId, reason: "killed" }
        });
      } catch {
      }
      entry.events.close();
      if (ci && ci.sessionIds.size === 0 && ci.pendingRestoreIds.size === 0) {
        await startIdleTimer(ci, `killSession "${sessionId}"`);
      }
    },
    async detachClient(sessionId, clientId) {
      const entry = byId.get(sessionId);
      if (!entry) return;
      if (entry.attachCount > 0) entry.attachCount--;
      unregisterClient(entry, clientId);
      if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
        await this.killSession(sessionId).catch(() => {
        });
      } else if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && !entry.promptActive) {
        await closeSessionImpl(sessionId, void 0, {
          reason: "last_client_detached"
        }).catch((err) => {
          writeStderrLine(
            `qwen serve: close-on-last-detach failed for ${JSON.stringify(sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
          );
        });
      }
    },
    killAllSync() {
      shuttingDown = true;
      cancelIdleTimer();
      stopSessionReaper();
      const channels = Array.from(aliveChannels);
      defaultEntry = void 0;
      byId.clear();
      for (const info of channels) {
        try {
          info.channel.killSync();
        } catch {
        }
      }
    },
    async shutdown() {
      shuttingDown = true;
      cancelIdleTimer();
      stopSessionReaper();
      const entries = Array.from(byId.values());
      const channels = Array.from(aliveChannels);
      for (const ci of channels) ci.isDying = true;
      for (const e of entries) {
        permissionMediator.forgetSession(e.sessionId);
        e.pendingPermissionIds.clear();
      }
      defaultEntry = void 0;
      byId.clear();
      for (const e of entries) {
        telemetry.metrics?.sessionLifecycle("die");
        try {
          e.events.publish({
            type: "session_died",
            data: { sessionId: e.sessionId, reason: "daemon_shutdown" }
          });
        } catch {
        }
        e.events.close();
      }
      const inFlightSessionAwaits = Array.from(inFlightSpawns.values()).map(
        (p) => p.then(
          () => void 0,
          () => void 0
        )
      );
      const inFlightRestoreAwaits = Array.from(inFlightRestores.values()).map(
        (restore) => restore.promise.then(
          () => void 0,
          () => void 0
        )
      );
      const inFlightChannelAwait = inFlightChannelSpawn ? inFlightChannelSpawn.then(
        () => void 0,
        () => void 0
      ) : Promise.resolve();
      await Promise.all([
        ...channels.map((ci) => ci.channel.kill().catch(() => {
        })),
        ...inFlightSessionAwaits,
        ...inFlightRestoreAwaits,
        inFlightChannelAwait
      ]);
    },
    async preheat() {
      if (shuttingDown) return;
      const ci = await ensureChannel();
      const idleMs = resolvedChannelIdleTimeoutMs();
      if (idleMs > 0 && ci.sessionIds.size === 0 && ci.pendingRestoreIds.size === 0) {
        await startIdleTimer(ci);
      }
    }
  };
}
__name(createAcpSessionBridge, "createAcpSessionBridge");
async function withTimeout(p, ms, label) {
  let timer;
  const timeoutP = new Promise((_, reject) => {
    timer = setTimeout(() => reject(new BridgeTimeoutError(label, ms)), ms);
  });
  try {
    return await Promise.race([p, timeoutP]);
  } finally {
    if (timer) clearTimeout(timer);
  }
}
__name(withTimeout, "withTimeout");
var createHttpAcpBridge = createAcpSessionBridge;

export {
  extractErrorMessage,
  extractErrorCode,
  createAcpSessionBridge,
  createHttpAcpBridge
};
/**
 * @license
 * Copyright 2025 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
