// Force strict mode and setup for ESM
"use strict";
import {
  MCP_RESTART_SERVER_DEADLINE_MS
} from "./chunk-P7M4GA57.js";
import {
  BranchWhilePromptActiveError,
  CancelSentinelCollisionError,
  CdWhilePromptActiveError,
  InvalidClientIdError,
  InvalidPermissionOptionError,
  InvalidRewindTargetError,
  InvalidSessionMetadataError,
  InvalidSessionScopeError,
  PermissionForbiddenError,
  PromptQueueFullError,
  RestoreInProgressError,
  SessionBusyError,
  SessionLimitExceededError,
  SessionNotFoundError,
  SessionShellClientRequiredError,
  SessionShellDisabledError,
  WorkspaceMismatchError,
  isNotCurrentlyGeneratingCancelError
} from "./chunk-T22H4V4R.js";
import {
  MID_TURN_QUEUE_DRAIN_METHOD
} from "./chunk-EWALXUAD.js";
import {
  canonicalizeWorkspace
} from "./chunk-SEZC2725.js";
import {
  DEFAULT_RING_SIZE,
  EVENT_SCHEMA_VERSION,
  EventBus
} from "./chunk-ALX6BUYW.js";
import {
  ClientSideConnection,
  PROTOCOL_VERSION,
  RequestError
} from "./chunk-VRDMOSQS.js";
import {
  defaultSpawnChannelFactory
} from "./chunk-4GROR3CI.js";
import {
  BridgeChannelClosedError,
  BridgeTimeoutError,
  SERVE_CONTROL_EXT_METHODS,
  SERVE_STATUS_EXT_METHODS,
  STATUS_SCHEMA_VERSION,
  createIdleWorkspaceExtensionsStatus,
  createIdleWorkspaceHooksStatus
} from "./chunk-7MFZZRJS.js";
import {
  ShellExecutionService,
  TrustGateError
} from "./chunk-IQ7FOOIH.js";
import {
  DAEMON_TRACEPARENT_META_KEY,
  DAEMON_TRACESTATE_META_KEY
} from "./chunk-4IUH45NF.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 path3 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 oldest2 = this.resolvedOrder.shift();
      if (oldest2 !== void 0) this.resolved.delete(oldest2);
    }
  }
  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
var PUBLISH_ARTIFACT_TOOL_NAME = "artifact";
function isFsErrorShape(err) {
  return err instanceof Error && err.name === "FsError" && typeof err.kind === "string";
}
__name(isFsErrorShape, "isFsErrorShape");
function isRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
__name(isRecord, "isRecord");
function artifactPayloadFields(artifact) {
  return {
    title: artifact["title"],
    kind: artifact["kind"],
    storage: artifact["storage"],
    description: artifact["description"],
    workspacePath: artifact["workspacePath"],
    managedId: artifact["managedId"],
    url: artifact["url"],
    mimeType: artifact["mimeType"],
    sizeBytes: artifact["sizeBytes"],
    metadata: artifact["metadata"]
  };
}
__name(artifactPayloadFields, "artifactPayloadFields");
function extractCappedArtifactInputs(rawArtifacts, limit, sessionId, source, toInput) {
  const artifacts = [];
  for (let index = 0; index < rawArtifacts.length; index++) {
    const artifact = rawArtifacts[index];
    if (!isRecord(artifact)) {
      writeStderrLine(
        `[artifacts] session=${sessionId} action=dropped reason=malformed source=${source} index=${index}`
      );
      continue;
    }
    if (artifacts.length >= limit) {
      writeStderrLine(
        `[artifacts] session=${sessionId} action=dropped reason="artifact batch limit exceeded" source=${source} dropped=${rawArtifacts.length - index}`
      );
      break;
    }
    artifacts.push(toInput(artifact));
  }
  return artifacts;
}
__name(extractCappedArtifactInputs, "extractCappedArtifactInputs");
function artifactIngestionErrorReason(error) {
  if (!(error instanceof Error)) {
    return String(error);
  }
  return {
    name: error.name,
    message: error.message,
    stack: error.stack?.split("\n").slice(0, 4).join("\n")
  };
}
__name(artifactIngestionErrorReason, "artifactIngestionErrorReason");
function extractSessionUpdateArtifacts(params, updateMeta, limit, sessionId) {
  const rawArtifacts = updateMeta?.["artifacts"];
  if (!Array.isArray(rawArtifacts)) {
    return [];
  }
  const update = params.update;
  if (update.sessionUpdate !== "tool_call_update" || update.status !== "completed" && update.status !== "failed" && update.status !== "cancelled") {
    return [];
  }
  const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : void 0;
  const toolName = typeof updateMeta?.["toolName"] === "string" ? updateMeta["toolName"] : void 0;
  return extractCappedArtifactInputs(
    rawArtifacts,
    limit,
    sessionId,
    "tool",
    (artifact) => ({
      ...artifactPayloadFields(artifact),
      source: "tool",
      toolCallId,
      toolName
    })
  );
}
__name(extractSessionUpdateArtifacts, "extractSessionUpdateArtifacts");
function sanitizeSessionUpdateArtifacts(params, updateMeta) {
  if (!Array.isArray(updateMeta?.["artifacts"])) {
    return params;
  }
  const sanitizedMeta = { ...updateMeta };
  delete sanitizedMeta["artifacts"];
  const update = {
    ...params.update,
    _meta: sanitizedMeta
  };
  return {
    ...params,
    update
  };
}
__name(sanitizeSessionUpdateArtifacts, "sanitizeSessionUpdateArtifacts");
function isTrustedArtifactToolUpdate(params, updateMeta) {
  const update = params.update;
  return update.sessionUpdate === "tool_call_update" && update.status === "completed" && updateMeta?.["toolName"] === PUBLISH_ARTIFACT_TOOL_NAME;
}
__name(isTrustedArtifactToolUpdate, "isTrustedArtifactToolUpdate");
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, clientMcpSender, ownsSession = () => true) {
    this.resolveEntry = resolveEntry;
    this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
    this.mediator = mediator;
    this.permissionTimeoutMs = permissionTimeoutMs;
    this.maxPendingPerSession = maxPendingPerSession;
    this.fileSystem = fileSystem;
    this.onModelPromoted = onModelPromoted;
    this.onModePromoted = onModePromoted;
    this.clientMcpSender = clientMcpSender;
    this.ownsSession = ownsSession;
  }
  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) {
    if (!this.ownsSession(params.sessionId) && !this.inFlightRestoreIds.has(params.sessionId)) {
      writeStderrLine(
        `[demux] session=${params.sessionId} type=session_update action=dropped reason=session_not_owned`
      );
      return;
    }
    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;
    const artifacts = entry?.artifacts ? extractSessionUpdateArtifacts(
      params,
      updateMeta,
      entry.artifacts.inputBatchLimit(),
      entry.sessionId
    ) : [];
    const publishParams = sanitizeSessionUpdateArtifacts(params, updateMeta);
    events.publish({
      type: "session_update",
      data: publishParams,
      ...originator,
      ...serverTimestamp !== void 0 ? { _meta: { serverTimestamp } } : {}
    });
    if (entry) {
      if (artifacts.length > 0) {
        await this.upsertAndPublishArtifacts(entry, artifacts, {
          trustedPublisher: isTrustedArtifactToolUpdate(params, updateMeta)
        });
      }
    }
  }
  /**
   * 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 === SERVE_CONTROL_EXT_METHODS.clientMcpMessage) {
      return this.handleClientMcpMessage(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 };
  }
  /**
   * Reverse tool channel (issue #5626, Phase 2) — answer the child's
   * `qwen/control/client_mcp/message` ext-method. The child's session
   * `McpClientManager` calls this when its agent drives a client-hosted
   * (extension) MCP server: `params` carries the advertised `server` name and
   * the JSON-RPC `payload` (initialize / tools/list / tools/call / a
   * notification). We resolve the per-WS-connection sender via the injected
   * `clientMcpSender` lookup, deliver the payload over the daemon WS, and
   * return the correlated response as `{ payload }`.
   *
   * Rejects with ACP `methodNotFound` when no `clientMcpSender` is wired (Mode
   * A / tests can't host a client MCP server), and `invalidParams` when the
   * frame is malformed or the named server is no longer hosted (e.g. the
   * extension disconnected mid-turn) — the agent's `SdkControlClientTransport`
   * surfaces that as a transport error rather than hanging.
   */
  async handleClientMcpMessage(params) {
    if (!this.clientMcpSender) {
      throw RequestError.methodNotFound(
        SERVE_CONTROL_EXT_METHODS.clientMcpMessage
      );
    }
    const server = params["server"];
    if (typeof server !== "string" || server.length === 0) {
      throw RequestError.invalidParams(
        void 0,
        "`server` must be a non-empty string"
      );
    }
    const payload = params["payload"];
    if (payload === null || typeof payload !== "object") {
      throw RequestError.invalidParams(
        void 0,
        "`payload` must be a JSON-RPC message object"
      );
    }
    const send = this.clientMcpSender(server);
    if (!send) {
      throw RequestError.invalidParams(
        void 0,
        `client-hosted MCP server '${server}' is not currently connected`
      );
    }
    const response = await send(payload);
    return { payload: response };
  }
  /**
   * Handle child->bridge ACP `extNotification` calls. Recognized methods are
   * `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/artifact-event` (hook artifacts),
   * `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/artifact-event") {
      await this.handleArtifactEvent(params);
      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);
  }
  async handleArtifactEvent(params) {
    const sessionId = params["sessionId"];
    const rawArtifacts = params["artifacts"];
    if (typeof sessionId !== "string" || !Array.isArray(rawArtifacts)) {
      writeStderrLine(
        `[demux] session=${typeof sessionId === "string" ? sessionId : "<missing>"} type=artifact_event action=dropped reason=malformed`
      );
      return;
    }
    if (!this.ownsSession(sessionId) && !this.inFlightRestoreIds.has(sessionId)) {
      writeStderrLine(
        `[demux] session=${sessionId} type=artifact_event action=dropped reason=session_not_owned`
      );
      return;
    }
    const entry = this.resolveEntry(sessionId);
    if (!entry) {
      writeStderrLine(
        `[demux] session=${sessionId} type=artifact_event action=dropped reason=session_not_found`
      );
      return;
    }
    const hookEventName = typeof params["hookEventName"] === "string" ? params["hookEventName"] : void 0;
    const toolName = typeof params["toolName"] === "string" ? params["toolName"] : void 0;
    const toolCallId = typeof params["toolCallId"] === "string" ? params["toolCallId"] : void 0;
    const artifacts = extractCappedArtifactInputs(
      rawArtifacts,
      entry.artifacts.inputBatchLimit(),
      entry.sessionId,
      "hook",
      (artifact) => ({
        ...artifactPayloadFields(artifact),
        source: "hook",
        hookEventName,
        toolName,
        toolCallId
      })
    );
    await this.upsertAndPublishArtifacts(entry, artifacts);
  }
  async upsertAndPublishArtifacts(entry, artifacts, options) {
    try {
      const result = await entry.artifacts.upsertMany(artifacts, options);
      this.publishArtifactChanges(entry, result.changes);
    } catch (error) {
      writeStderrLine(
        `[artifacts] session=${entry.sessionId} action=dropped reason=${JSON.stringify(
          artifactIngestionErrorReason(error)
        )}`
      );
    }
  }
  publishArtifactChanges(entry, changes) {
    for (const change of changes) {
      entry.events.publish({
        type: "artifact_changed",
        data: { sessionId: entry.sessionId, change },
        ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
      });
    }
  }
  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/sessionArtifacts.ts
init_esbuild_shims();
import { createHash } from "node:crypto";
import { promises as fs2 } from "node:fs";
import path2 from "node:path";
var SOURCE_RESERVATIONS = {
  tool: 100,
  client: 50,
  hook: 50
};
var WORKSPACE_STATUS_REFRESH_TTL_MS = 5e3;
var WORKSPACE_STATUS_REFRESH_BATCH_SIZE = 20;
var SessionArtifactValidationError = class extends Error {
  constructor(message, field) {
    super(message);
    this.field = field;
    this.name = "SessionArtifactValidationError";
  }
  static {
    __name(this, "SessionArtifactValidationError");
  }
  code = "VALIDATION_FAILED";
};
var SessionArtifactStore = class {
  static {
    __name(this, "SessionArtifactStore");
  }
  sessionId;
  workspaceCwd;
  maxArtifacts;
  artifacts = /* @__PURE__ */ new Map();
  receivedSeq = 0;
  insertSeq = 0;
  realWorkspaceCwdPromise;
  operationQueue = Promise.resolve();
  constructor(options) {
    this.sessionId = options.sessionId;
    this.workspaceCwd = options.workspaceCwd;
    this.maxArtifacts = options.maxArtifacts ?? 200;
  }
  inputBatchLimit() {
    return this.maxArtifacts * 2;
  }
  async list() {
    return this.enqueue(async () => {
      await this.refreshWorkspaceStatuses();
      return {
        v: 1,
        sessionId: this.sessionId,
        artifacts: Array.from(this.artifacts.values()).sort((a, b) => a.insertSeq - b.insertSeq).map(toPublicArtifact),
        generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
        limits: { maxArtifacts: this.maxArtifacts }
      };
    });
  }
  async upsertMany(inputs, options = {}) {
    return this.enqueue(async () => {
      const normalizedResults = [];
      for (const input of inputs) {
        try {
          normalizedResults.push(
            await this.normalizeInput(
              input,
              ++this.receivedSeq,
              options.trustedPublisher === true
            )
          );
        } catch (error) {
          if (options.strict) {
            throw error;
          }
          const message = error instanceof Error ? error.message : String(error);
          writeStderrLine(
            `[artifacts] session=${this.sessionId} action=dropped reason=${JSON.stringify(
              message
            )}`
          );
        }
      }
      const changes = [];
      for (const artifact of coalesceByIdentity(normalizedResults)) {
        const existing = this.artifacts.get(artifact.id) ?? this.findPublishedUpgradeTarget(artifact) ?? this.findPublishedWorkspaceTarget(artifact);
        if (!existing) {
          const stored = {
            ...artifact,
            insertSeq: ++this.insertSeq
          };
          this.artifacts.set(stored.id, stored);
          changes.push({
            action: "created",
            artifactId: stored.id,
            artifact: toPublicArtifact(stored)
          });
          continue;
        }
        const updated = mergeArtifact(existing, artifact);
        if (updated.changed) {
          if (updated.artifact.id !== existing.id) {
            this.artifacts.delete(existing.id);
          }
          this.artifacts.set(updated.artifact.id, updated.artifact);
          changes.push({
            action: "updated",
            artifactId: updated.artifact.id,
            artifact: toPublicArtifact(updated.artifact)
          });
        }
      }
      const createdIds = new Set(
        changes.filter((change) => change.action === "created").map((change) => change.artifactId)
      );
      changes.push(...await this.evictOverflow(createdIds, changes));
      return { v: 1, sessionId: this.sessionId, changes };
    });
  }
  findPublishedUpgradeTarget(artifact) {
    if (artifact.storage !== "published" || !artifact.trustedPublisher || !artifact.managedId || !artifact.url) {
      return void 0;
    }
    const byUrl = this.artifacts.get(
      stableArtifactId(this.sessionId, `url:${artifact.url}`)
    );
    if (byUrl && (byUrl.storage !== "published" || byUrl.managedId === artifact.managedId)) {
      return byUrl;
    }
    for (const existing of this.artifacts.values()) {
      if (existing.storage === "workspace" && existing.workspacePath && artifact.managedId === managedIdForWorkspacePath(
        this.workspaceCwd,
        existing.workspacePath
      ) || existing.storage === "published" && existing.managedId === artifact.managedId) {
        return existing;
      }
    }
    return void 0;
  }
  findPublishedWorkspaceTarget(artifact) {
    if (artifact.storage !== "workspace" || !artifact.workspacePath) {
      return void 0;
    }
    const managedId = managedIdForWorkspacePath(
      this.workspaceCwd,
      artifact.workspacePath
    );
    for (const existing of this.artifacts.values()) {
      if (existing.storage === "published" && existing.managedId === managedId) {
        return existing;
      }
    }
    return void 0;
  }
  async remove(artifactId, options) {
    return this.enqueue(async () => {
      const existing = this.artifacts.get(artifactId);
      if (!existing) {
        return { v: 1, sessionId: this.sessionId, changes: [] };
      }
      if (existing.source === "client" && existing.clientId !== void 0 && existing.clientId !== options?.clientId) {
        writeStderrLine(
          `[artifacts] session=${this.sessionId} action=remove_denied artifactId=${artifactId} owner=${existing.clientId} requester=${options?.clientId ?? "<anonymous>"}`
        );
        return { v: 1, sessionId: this.sessionId, changes: [] };
      }
      this.artifacts.delete(artifactId);
      return {
        v: 1,
        sessionId: this.sessionId,
        changes: [
          {
            action: "removed",
            artifactId,
            artifact: toPublicArtifact(existing),
            reason: "explicit"
          }
        ]
      };
    });
  }
  enqueue(operation) {
    const result = this.operationQueue.then(operation, operation);
    this.operationQueue = result.then(
      () => void 0,
      () => void 0
    );
    return result;
  }
  async normalizeInput(input, receivedSeq, trustedPublisherFromCaller) {
    if (!input || typeof input !== "object") {
      throw new SessionArtifactValidationError("Artifact must be an object");
    }
    const title = normalizeString(input.title, "title", 200, true);
    const description = normalizeString(
      input.description,
      "description",
      1e3,
      false
    );
    const source = input.source ?? "tool";
    if (source !== "tool" && source !== "hook" && source !== "client") {
      throw new SessionArtifactValidationError(
        "source must be tool, hook, or client",
        "source"
      );
    }
    const trustedPublisher = trustedPublisherFromCaller;
    const workspacePath = input.workspacePath ? normalizeWorkspacePath(input.workspacePath, this.workspaceCwd) : void 0;
    const managedId = normalizeManagedId(input.managedId);
    const rawStorage = input.storage;
    const storage = inferStorage(rawStorage, {
      workspacePath,
      managedId,
      url: input.url,
      trustedPublisher
    });
    const url = input.url ? normalizeArtifactUrl(input.url, trustedPublisher) : void 0;
    validateLocator(storage, {
      workspacePath,
      managedId,
      url,
      trustedPublisher
    });
    const metadata = normalizeMetadata(input.metadata);
    const workspaceStatus = workspacePath ? await this.getInitialWorkspaceStatus(workspacePath) : void 0;
    if (workspaceStatus?.escaped) {
      throw new SessionArtifactValidationError(
        "workspacePath must stay inside the workspace",
        "workspacePath"
      );
    }
    const kind = normalizeKind(
      input.kind ?? inferKind({ storage, workspacePath, url })
    );
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const identityKey = buildIdentityKey({
      storage,
      workspacePath,
      managedId,
      url
    });
    const id = stableArtifactId(this.sessionId, identityKey);
    return {
      id,
      identityKey,
      receivedSeq,
      retentionSource: source,
      trustedPublisher,
      kind,
      storage,
      source,
      status: workspaceStatus?.status ?? "available",
      ...workspacePath ? { lastStatAt: Date.now() } : {},
      title,
      description,
      workspacePath,
      managedId,
      url,
      mimeType: normalizeString(input.mimeType, "mimeType", 120, false),
      sizeBytes: input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) : workspaceStatus?.sizeBytes,
      metadata,
      clientRetained: source === "client",
      createdAt: now,
      updatedAt: now,
      toolCallId: normalizeString(input.toolCallId, "toolCallId", 200, false),
      toolName: normalizeString(input.toolName, "toolName", 200, false),
      hookEventName: normalizeString(
        input.hookEventName,
        "hookEventName",
        200,
        false
      ),
      clientId: normalizeString(input.clientId, "clientId", 200, false)
    };
  }
  async refreshWorkspaceStatuses() {
    const now = Date.now();
    const staleWorkspaceArtifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.workspacePath).filter((artifact) => shouldRefreshWorkspaceStatus(artifact, now));
    await runInBatches(
      staleWorkspaceArtifacts,
      WORKSPACE_STATUS_REFRESH_BATCH_SIZE,
      (artifact) => this.refreshWorkspaceStatus(artifact, {
        onError: "missing",
        now
      })
    );
  }
  async getInitialWorkspaceStatus(workspacePath) {
    try {
      return await getWorkspaceStatus(
        this.workspaceCwd,
        workspacePath,
        this.getRealWorkspaceCwd()
      );
    } catch (error) {
      const reason = error instanceof Error ? error.message : String(error);
      throw new SessionArtifactValidationError(
        `workspacePath could not be inspected: ${reason}`,
        "workspacePath"
      );
    }
  }
  async refreshWorkspaceStatus(artifact, options) {
    if (!artifact.workspacePath) {
      return;
    }
    try {
      const status = await getWorkspaceStatus(
        this.workspaceCwd,
        artifact.workspacePath,
        this.getRealWorkspaceCwd()
      );
      artifact.status = status.status;
      artifact.sizeBytes = status.sizeBytes;
      if (status.escaped) {
        artifact.status = "missing";
        artifact.sizeBytes = void 0;
        delete artifact.workspacePath;
      }
      artifact.lastStatAt = options.now ?? Date.now();
    } catch (error) {
      writeStderrLine(
        `[artifacts] session=${this.sessionId} action=status_refresh_failed artifactId=${artifact.id} reason=${JSON.stringify(
          error instanceof Error ? error.message : String(error)
        )}`
      );
      if (options.onError === "missing") {
        artifact.status = "missing";
        artifact.sizeBytes = void 0;
        artifact.lastStatAt = options.now ?? Date.now();
      }
      return;
    }
  }
  getRealWorkspaceCwd() {
    if (!this.realWorkspaceCwdPromise) {
      const promise = fs2.realpath(this.workspaceCwd).catch((error) => {
        if (this.realWorkspaceCwdPromise === promise) {
          this.realWorkspaceCwdPromise = void 0;
        }
        throw error;
      });
      this.realWorkspaceCwdPromise = promise;
    }
    return this.realWorkspaceCwdPromise;
  }
  async evictOverflow(createdIds, changes) {
    const removed = [];
    if (this.artifacts.size <= this.maxArtifacts) {
      return removed;
    }
    const createdInThisBatch = new Set(createdIds);
    const candidates = Array.from(this.artifacts.values()).filter(
      (artifact) => !createdInThisBatch.has(artifact.id)
    );
    const now = Date.now();
    const staleWorkspaceCandidates = candidates.filter((artifact) => artifact.workspacePath).filter((artifact) => shouldRefreshWorkspaceStatus(artifact, now));
    await runInBatches(
      staleWorkspaceCandidates,
      WORKSPACE_STATUS_REFRESH_BATCH_SIZE,
      (artifact) => this.refreshWorkspaceStatus(artifact, { onError: "preserve", now })
    );
    const sourceCounts = countByRetentionSource(this.artifacts.values());
    while (this.artifacts.size > this.maxArtifacts) {
      const artifact = selectEvictionCandidate(candidates, sourceCounts);
      if (!artifact) break;
      this.artifacts.delete(artifact.id);
      candidates.splice(candidates.indexOf(artifact), 1);
      sourceCounts[artifact.retentionSource]--;
      removePriorChange(changes, artifact.id);
      removed.push({
        action: "removed",
        artifactId: artifact.id,
        artifact: toPublicArtifact(artifact),
        reason: "eviction"
      });
    }
    const overflowCreated = Array.from(this.artifacts.values()).filter((artifact) => createdInThisBatch.has(artifact.id)).sort((a, b) => b.receivedSeq - a.receivedSeq);
    for (const artifact of overflowCreated) {
      if (this.artifacts.size <= this.maxArtifacts) {
        break;
      }
      this.artifacts.delete(artifact.id);
      writeStderrLine(
        `[artifacts] session=${this.sessionId} action=dropped reason="max artifacts exceeded" artifactId=${artifact.id}`
      );
      removePriorChange(changes, artifact.id);
    }
    return removed;
  }
};
function coalesceByIdentity(artifacts) {
  const byId = /* @__PURE__ */ new Map();
  for (const artifact of artifacts) {
    const existing = byId.get(artifact.id);
    if (!existing) {
      byId.set(artifact.id, artifact);
      continue;
    }
    byId.set(artifact.id, mergeBatchArtifact(existing, artifact));
  }
  return Array.from(byId.values()).sort(
    (a, b) => a.receivedSeq - b.receivedSeq
  );
}
__name(coalesceByIdentity, "coalesceByIdentity");
function mergeBatchArtifact(existing, next) {
  const publishedUpgrade = existing.storage !== "published" && next.storage === "published" && next.trustedPublisher;
  if (publishedUpgrade) {
    const merged = {
      ...existing,
      id: next.id,
      identityKey: next.identityKey,
      kind: next.kind,
      storage: "published",
      status: next.status,
      title: next.title,
      description: next.description,
      managedId: next.managedId ?? existing.managedId,
      url: next.url ?? existing.url,
      mimeType: next.mimeType ?? existing.mimeType,
      sizeBytes: next.sizeBytes ?? existing.sizeBytes,
      metadata: mergeMetadata(existing, next),
      trustedPublisher: true,
      createdAt: existing.createdAt,
      receivedSeq: existing.receivedSeq,
      retentionSource: existing.retentionSource,
      clientRetained: existing.clientRetained || next.clientRetained,
      lastStatAt: void 0
    };
    delete merged.workspacePath;
    return merged;
  }
  return {
    ...existing,
    status: next.status,
    sizeBytes: mergeSizeBytes(existing, next),
    metadata: mergeMetadata(existing, next),
    clientRetained: existing.clientRetained || next.clientRetained,
    trustedPublisher: existing.trustedPublisher || next.trustedPublisher,
    lastStatAt: next.lastStatAt ?? existing.lastStatAt
  };
}
__name(mergeBatchArtifact, "mergeBatchArtifact");
function mergeArtifact(existing, incoming) {
  const now = (/* @__PURE__ */ new Date()).toISOString();
  const publishedUpgrade = existing.storage !== "published" && incoming.storage === "published" && incoming.trustedPublisher;
  const publishedRefresh = existing.storage === "published" && incoming.storage === "published" && incoming.trustedPublisher && incoming.managedId !== void 0 && incoming.managedId === existing.managedId;
  const publishedUpdate = publishedUpgrade || publishedRefresh;
  const next = {
    ...existing,
    id: publishedUpdate ? incoming.id : existing.id,
    identityKey: publishedUpdate ? incoming.identityKey : existing.identityKey,
    kind: publishedUpdate ? incoming.kind : existing.kind,
    storage: publishedUpgrade ? "published" : existing.storage,
    status: existing.storage === "published" && !publishedUpdate ? existing.status : incoming.status,
    managedId: publishedUpdate ? incoming.managedId ?? existing.managedId : existing.managedId,
    url: publishedUpdate ? incoming.url ?? existing.url : existing.url,
    workspacePath: publishedUpdate || existing.storage === "published" ? void 0 : existing.workspacePath ?? incoming.workspacePath,
    mimeType: publishedUpdate ? incoming.mimeType ?? existing.mimeType : existing.mimeType,
    sizeBytes: existing.storage === "published" && !publishedUpdate ? existing.sizeBytes : mergeSizeBytes(existing, incoming),
    metadata: existing.storage === "published" && !publishedUpdate ? existing.metadata : mergeMetadata(existing, incoming),
    source: existing.source,
    retentionSource: existing.retentionSource,
    trustedPublisher: existing.trustedPublisher || incoming.trustedPublisher,
    clientRetained: existing.clientRetained || incoming.clientRetained,
    lastStatAt: publishedUpdate || existing.storage === "published" ? void 0 : incoming.lastStatAt ?? existing.lastStatAt,
    updatedAt: existing.updatedAt
  };
  if (publishedUpdate) {
    next.title = incoming.title;
    next.description = incoming.description;
    delete next.workspacePath;
  }
  const changed = !publicArtifactsEqual(
    toPublicArtifact(existing),
    toPublicArtifact(next)
  );
  if (changed) {
    next.updatedAt = now;
  }
  return { artifact: changed ? next : existing, changed };
}
__name(mergeArtifact, "mergeArtifact");
function publicArtifactsEqual(a, b) {
  return a.id === b.id && a.kind === b.kind && a.storage === b.storage && a.source === b.source && a.status === b.status && a.title === b.title && a.description === b.description && a.workspacePath === b.workspacePath && a.managedId === b.managedId && a.url === b.url && a.mimeType === b.mimeType && a.sizeBytes === b.sizeBytes && metadataEqual(a.metadata, b.metadata) && a.clientRetained === b.clientRetained && a.createdAt === b.createdAt && a.toolCallId === b.toolCallId && a.toolName === b.toolName && a.hookEventName === b.hookEventName && a.clientId === b.clientId;
}
__name(publicArtifactsEqual, "publicArtifactsEqual");
function metadataEqual(a, b) {
  if (a === b) return true;
  if (!a || !b) return false;
  const aKeys = Object.keys(a);
  const bKeys = Object.keys(b);
  if (aKeys.length !== bKeys.length) return false;
  return aKeys.every((key) => Object.hasOwn(b, key) && a[key] === b[key]);
}
__name(metadataEqual, "metadataEqual");
function mergeSizeBytes(existing, incoming) {
  if (incoming.sizeBytes !== void 0) {
    return incoming.sizeBytes;
  }
  if (incoming.workspacePath && incoming.status === "missing") {
    return void 0;
  }
  return existing.sizeBytes;
}
__name(mergeSizeBytes, "mergeSizeBytes");
function mergeMetadata(existing, incoming) {
  if (!incoming.metadata || incoming.source === "hook" || incoming.source !== existing.source) {
    return existing.metadata;
  }
  const merged = { ...existing.metadata ?? {} };
  let changed = false;
  for (const [key, value] of Object.entries(incoming.metadata)) {
    if (!Object.hasOwn(merged, key)) {
      merged[key] = value;
      changed = true;
    }
  }
  if (!changed) {
    return existing.metadata;
  }
  if (!isMetadataWithinLimit(merged)) {
    writeStderrLine(
      `[artifacts] action=metadata_merge_dropped artifactId=${incoming.id} reason="metadata limit exceeded"`
    );
    return existing.metadata;
  }
  return merged;
}
__name(mergeMetadata, "mergeMetadata");
function isMetadataWithinLimit(metadata) {
  return Buffer.byteLength(JSON.stringify(metadata), "utf8") <= 4096;
}
__name(isMetadataWithinLimit, "isMetadataWithinLimit");
function countByRetentionSource(artifacts) {
  const counts = {
    tool: 0,
    client: 0,
    hook: 0
  };
  for (const artifact of artifacts) {
    counts[artifact.retentionSource]++;
  }
  return counts;
}
__name(countByRetentionSource, "countByRetentionSource");
function shouldRefreshWorkspaceStatus(artifact, now) {
  return artifact.lastStatAt === void 0 || now - artifact.lastStatAt >= WORKSPACE_STATUS_REFRESH_TTL_MS;
}
__name(shouldRefreshWorkspaceStatus, "shouldRefreshWorkspaceStatus");
function selectEvictionCandidate(candidates, sourceCounts) {
  return oldest(
    candidates,
    (artifact) => artifact.status === "missing" && !artifact.clientRetained
  ) ?? oldest(
    candidates,
    (artifact) => !artifact.clientRetained && sourceCounts[artifact.retentionSource] > SOURCE_RESERVATIONS[artifact.retentionSource]
  ) ?? oldest(candidates, (artifact) => !artifact.clientRetained) ?? oldest(candidates);
}
__name(selectEvictionCandidate, "selectEvictionCandidate");
function oldest(artifacts, predicate = () => true) {
  let selected;
  for (const artifact of artifacts) {
    if (!predicate(artifact)) {
      continue;
    }
    if (!selected || compareOldest(artifact, selected) < 0) {
      selected = artifact;
    }
  }
  return selected;
}
__name(oldest, "oldest");
function compareOldest(a, b) {
  const created = a.createdAt.localeCompare(b.createdAt);
  if (created !== 0) return created;
  return a.insertSeq - b.insertSeq;
}
__name(compareOldest, "compareOldest");
function removePriorChange(changes, artifactId) {
  const index = changes.findIndex((change) => change.artifactId === artifactId);
  if (index >= 0) {
    changes.splice(index, 1);
  }
}
__name(removePriorChange, "removePriorChange");
function toPublicArtifact(artifact) {
  const {
    id,
    kind,
    storage,
    source,
    status,
    title,
    description,
    workspacePath,
    managedId,
    url,
    mimeType,
    sizeBytes,
    metadata,
    clientRetained,
    createdAt,
    updatedAt,
    toolCallId,
    toolName,
    hookEventName,
    clientId
  } = artifact;
  return {
    id,
    kind,
    storage,
    source,
    status,
    title,
    ...description ? { description } : {},
    ...workspacePath ? { workspacePath } : {},
    ...managedId ? { managedId } : {},
    ...url ? { url } : {},
    ...mimeType ? { mimeType } : {},
    ...sizeBytes !== void 0 ? { sizeBytes } : {},
    ...metadata ? { metadata } : {},
    clientRetained,
    createdAt,
    updatedAt,
    ...toolCallId ? { toolCallId } : {},
    ...toolName ? { toolName } : {},
    ...hookEventName ? { hookEventName } : {},
    ...clientId ? { clientId } : {}
  };
}
__name(toPublicArtifact, "toPublicArtifact");
function inferStorage(requested, locators) {
  if (requested !== void 0 && typeof requested !== "string") {
    throw new SessionArtifactValidationError(
      "storage must be a string",
      "storage"
    );
  }
  if (requested) {
    if (requested !== "workspace" && requested !== "external_url" && requested !== "managed" && requested !== "published") {
      throw new SessionArtifactValidationError(
        "storage must be workspace, external_url, managed, or published",
        "storage"
      );
    }
    return requested;
  }
  if (locators.workspacePath) return "workspace";
  if (locators.managedId) return "managed";
  if (locators.url && locators.trustedPublisher) return "published";
  return "external_url";
}
__name(inferStorage, "inferStorage");
function normalizeKind(kind) {
  if (kind === "file" || kind === "link" || kind === "html" || kind === "image" || kind === "video" || kind === "audio" || kind === "pdf" || kind === "notebook" || kind === "other") {
    return kind;
  }
  throw new SessionArtifactValidationError(
    "kind must be a supported artifact kind",
    "kind"
  );
}
__name(normalizeKind, "normalizeKind");
function validateLocator(storage, locators) {
  if (storage === "published") {
    if (!locators.trustedPublisher) {
      throw new SessionArtifactValidationError(
        "published artifacts are reserved for trusted publishers",
        "storage"
      );
    }
    if (!locators.url) {
      throw new SessionArtifactValidationError(
        "published artifacts require url",
        "url"
      );
    }
    if (locators.workspacePath) {
      throw new SessionArtifactValidationError(
        "published artifacts cannot include workspacePath",
        "workspacePath"
      );
    }
    return;
  }
  const locatorCount = [
    locators.workspacePath,
    locators.managedId,
    locators.url
  ].filter(Boolean).length;
  if (locatorCount !== 1) {
    throw new SessionArtifactValidationError(
      "provide exactly one of workspacePath, managedId, or url"
    );
  }
  if (storage === "workspace" && !locators.workspacePath) {
    throw new SessionArtifactValidationError(
      "workspace storage requires workspacePath",
      "workspacePath"
    );
  }
  if (storage === "managed" && !locators.managedId) {
    throw new SessionArtifactValidationError(
      "managed storage requires managedId",
      "managedId"
    );
  }
  if (storage === "external_url" && !locators.url) {
    throw new SessionArtifactValidationError(
      "external_url storage requires url",
      "url"
    );
  }
}
__name(validateLocator, "validateLocator");
function buildIdentityKey(input) {
  if (input.workspacePath) return `workspace:${input.workspacePath}`;
  if (input.managedId) return `managed:${input.managedId}`;
  if (input.url) return `url:${input.url}`;
  throw new SessionArtifactValidationError(
    "artifact identity requires workspacePath, managedId, or url"
  );
}
__name(buildIdentityKey, "buildIdentityKey");
function stableArtifactId(sessionId, identityKey) {
  return createHash("sha256").update(`${sessionId}:${identityKey}`).digest("hex").slice(0, 16);
}
__name(stableArtifactId, "stableArtifactId");
function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
  return createHash("sha1").update(path2.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
}
__name(managedIdForWorkspacePath, "managedIdForWorkspacePath");
function normalizeString(value, field, maxLength, required) {
  if (value === void 0 || value === null) {
    if (required) {
      throw new SessionArtifactValidationError(`${field} is required`, field);
    }
    return void 0;
  }
  if (typeof value !== "string") {
    throw new SessionArtifactValidationError(
      `${field} must be a string`,
      field
    );
  }
  const trimmed = value.trim();
  if (!trimmed) {
    if (required) {
      throw new SessionArtifactValidationError(`${field} is required`, field);
    }
    return void 0;
  }
  if (trimmed.length > maxLength) {
    throw new SessionArtifactValidationError(
      `${field} exceeds ${maxLength} characters`,
      field
    );
  }
  if (hasControlCharacter(trimmed, field === "description")) {
    throw new SessionArtifactValidationError(
      `${field} contains control characters`,
      field
    );
  }
  if (isDisplayField(field) && hasUnsafeDisplayPayload(trimmed)) {
    throw new SessionArtifactValidationError(
      `${field} contains unsafe markup`,
      field
    );
  }
  return trimmed;
}
__name(normalizeString, "normalizeString");
function normalizeManagedId(value) {
  const managedId = normalizeString(value, "managedId", 200, false);
  if (!managedId) return void 0;
  if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path2.isAbsolute(managedId) || path2.win32.isAbsolute(managedId)) {
    throw new SessionArtifactValidationError(
      "managedId must be an opaque managed resource id",
      "managedId"
    );
  }
  return managedId;
}
__name(normalizeManagedId, "normalizeManagedId");
function isDisplayField(field) {
  return field === "title" || field === "description" || field === "mimeType" || field === "workspacePath" || field === "managedId";
}
__name(isDisplayField, "isDisplayField");
function hasControlCharacter(value, allowLineWhitespace = false) {
  for (let i = 0; i < value.length; i++) {
    const code = value.charCodeAt(i);
    if (allowLineWhitespace && (code === 9 || code === 10 || code === 13)) {
      continue;
    }
    if (code <= 31 || code === 127 || code >= 8203 && code <= 8207 || code === 8232 || code === 8233 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297 || code === 65279) {
      return true;
    }
  }
  return false;
}
__name(hasControlCharacter, "hasControlCharacter");
function hasUnsafeDisplayPayload(value) {
  return /<\s*\/?[a-z!]|&(?:#[0-9]+|#x[0-9a-f]+|[a-z][a-z0-9]+);|javascript\s*:|data\s*:\s*(?:text\/(?:html|javascript)|application\/javascript|image\/svg\+xml)/i.test(
    value
  ) || /(?:^|[\s"'`<])on[a-z][a-z0-9-]*\s*=/i.test(value);
}
__name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
function normalizeWorkspacePath(raw, workspaceCwd) {
  const trimmed = normalizeString(raw, "workspacePath", 500, true);
  if (path2.isAbsolute(trimmed)) {
    throw new SessionArtifactValidationError(
      "workspacePath must be relative to the workspace",
      "workspacePath"
    );
  }
  const absolute = path2.resolve(workspaceCwd, trimmed);
  const relative = path2.relative(workspaceCwd, absolute);
  if (!relative || isOutsidePath(relative)) {
    throw new SessionArtifactValidationError(
      "workspacePath must stay inside the workspace",
      "workspacePath"
    );
  }
  return relative.split(path2.sep).join("/");
}
__name(normalizeWorkspacePath, "normalizeWorkspacePath");
function normalizeArtifactUrl(raw, allowFile) {
  const trimmed = normalizeString(raw, "url", 2048, true);
  let parsed;
  try {
    parsed = new URL(trimmed);
  } catch {
    throw new SessionArtifactValidationError("url must be valid", "url");
  }
  if (parsed.username || parsed.password) {
    throw new SessionArtifactValidationError(
      "url must not include credentials",
      "url"
    );
  }
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:" && !(allowFile && parsed.protocol === "file:")) {
    throw new SessionArtifactValidationError(
      allowFile ? "url must use http, https, or file" : "url must use http or https",
      "url"
    );
  }
  return parsed.href;
}
__name(normalizeArtifactUrl, "normalizeArtifactUrl");
function normalizeMetadata(metadata) {
  if (metadata === void 0) {
    return void 0;
  }
  if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
    throw new SessionArtifactValidationError(
      "metadata must be an object",
      "metadata"
    );
  }
  const normalized = {};
  for (const [key, value] of Object.entries(metadata)) {
    if (!key) {
      throw new SessionArtifactValidationError(
        "metadata keys must not be empty",
        "metadata"
      );
    }
    if (key.length > 120) {
      throw new SessionArtifactValidationError(
        "metadata keys must be 120 characters or fewer",
        "metadata"
      );
    }
    if (hasControlCharacter(key) || hasUnsafeDisplayPayload(key)) {
      throw new SessionArtifactValidationError(
        "metadata keys contain unsafe content",
        "metadata"
      );
    }
    if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
      throw new SessionArtifactValidationError(
        "metadata values must be primitive",
        "metadata"
      );
    }
    if (typeof value === "number" && !Number.isFinite(value)) {
      throw new SessionArtifactValidationError(
        "metadata numbers must be finite",
        "metadata"
      );
    }
    if (typeof value === "string" && (hasControlCharacter(value) || hasUnsafeDisplayPayload(value))) {
      throw new SessionArtifactValidationError(
        "metadata string values contain unsafe content",
        "metadata"
      );
    }
    normalized[key] = value;
  }
  if (Object.keys(normalized).length === 0) {
    return void 0;
  }
  if (Buffer.byteLength(JSON.stringify(normalized), "utf8") > 4096) {
    throw new SessionArtifactValidationError(
      "metadata must be 4096 bytes or fewer",
      "metadata"
    );
  }
  return normalized;
}
__name(normalizeMetadata, "normalizeMetadata");
function normalizeSizeBytes(value) {
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
    throw new SessionArtifactValidationError(
      "sizeBytes must be a non-negative safe integer",
      "sizeBytes"
    );
  }
  return value;
}
__name(normalizeSizeBytes, "normalizeSizeBytes");
function inferKind(input) {
  if (input.storage === "published") return "html";
  if (input.url) return "link";
  const ext = input.workspacePath ? path2.extname(input.workspacePath).toLowerCase() : "";
  if (ext === ".html" || ext === ".htm") return "html";
  if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
    return "image";
  }
  if ([".mp4", ".mov", ".webm"].includes(ext)) return "video";
  if ([".mp3", ".wav", ".m4a", ".ogg"].includes(ext)) return "audio";
  if (ext === ".pdf") return "pdf";
  if (ext === ".ipynb") return "notebook";
  return input.workspacePath ? "file" : "other";
}
__name(inferKind, "inferKind");
async function getWorkspaceStatus(workspaceCwd, workspacePath, realWorkspaceCwd) {
  const absolutePath = path2.resolve(workspaceCwd, workspacePath);
  const realWorkspace = await realWorkspaceCwd;
  try {
    const realPath = await fs2.realpath(absolutePath);
    const relative = path2.relative(realWorkspace, realPath);
    if (!relative || isOutsidePath(relative)) {
      return { status: "missing", escaped: true };
    }
    const stat = await fs2.stat(realPath);
    return {
      status: "available",
      ...stat.isFile() ? { sizeBytes: stat.size } : {}
    };
  } catch (error) {
    if (!isNotFoundError(error)) {
      throw error;
    }
    if (await danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace)) {
      return { status: "missing", escaped: true };
    }
    return { status: "missing" };
  }
}
__name(getWorkspaceStatus, "getWorkspaceStatus");
async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
  try {
    const stat = await fs2.lstat(absolutePath);
    if (!stat.isSymbolicLink()) {
      return false;
    }
    const target = await fs2.readlink(absolutePath);
    const parent = await fs2.realpath(path2.dirname(absolutePath));
    const targetPath = path2.resolve(parent, target);
    return isOutsidePath(path2.relative(realWorkspace, targetPath));
  } catch (error) {
    if (isNotFoundError(error)) {
      return false;
    }
    throw error;
  }
}
__name(danglingSymlinkEscapesWorkspace, "danglingSymlinkEscapesWorkspace");
async function runInBatches(items, batchSize, fn) {
  for (let index = 0; index < items.length; index += batchSize) {
    await Promise.all(items.slice(index, index + batchSize).map(fn));
  }
}
__name(runInBatches, "runInBatches");
function isNotFoundError(error) {
  if (typeof error !== "object" || error === null || !("code" in error)) {
    return false;
  }
  const code = error.code;
  return code === "ENOENT" || code === "ENOTDIR";
}
__name(isNotFoundError, "isNotFoundError");
function isOutsidePath(relative) {
  return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
}
__name(isOutsidePath, "isOutsidePath");

// 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 parseWorkspaceMemoryRememberResult(response) {
  if (response === null || typeof response !== "object" || Array.isArray(response)) {
    throw new Error("Malformed workspace memory remember response");
  }
  const record = response;
  const summary = record["summary"];
  const filesTouched = record["filesTouched"];
  const touchedScopes = record["touchedScopes"];
  if (summary !== void 0 && typeof summary !== "string" || !Array.isArray(filesTouched) || !filesTouched.every((file) => typeof file === "string") || !Array.isArray(touchedScopes) || !touchedScopes.every((scope) => scope === "user" || scope === "project")) {
    throw new Error("Malformed workspace memory remember response");
  }
  return {
    ...summary === void 0 ? {} : { summary },
    filesTouched,
    touchedScopes
  };
}
__name(parseWorkspaceMemoryRememberResult, "parseWorkspaceMemoryRememberResult");
function isBridgeAutoMemoryTopic(value) {
  return value === "user" || value === "feedback" || value === "project" || value === "reference";
}
__name(isBridgeAutoMemoryTopic, "isBridgeAutoMemoryTopic");
function parseWorkspaceMemoryForgetMatch(value) {
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
    return null;
  }
  const record = value;
  if (!isBridgeAutoMemoryTopic(record["topic"]) || typeof record["summary"] !== "string" || typeof record["filePath"] !== "string") {
    return null;
  }
  return {
    topic: record["topic"],
    summary: record["summary"],
    filePath: record["filePath"]
  };
}
__name(parseWorkspaceMemoryForgetMatch, "parseWorkspaceMemoryForgetMatch");
function parseWorkspaceMemoryForgetResult(response) {
  if (response === null || typeof response !== "object" || Array.isArray(response)) {
    throw new Error("Malformed workspace memory forget response");
  }
  const record = response;
  const summary = record["summary"];
  const removedEntries = record["removedEntries"];
  const touchedTopics = record["touchedTopics"];
  const parsedRemovedEntries = Array.isArray(removedEntries) ? removedEntries.map(parseWorkspaceMemoryForgetMatch) : [];
  if (summary !== void 0 && typeof summary !== "string" || !Array.isArray(removedEntries) || parsedRemovedEntries.some((entry) => entry === null) || !Array.isArray(touchedTopics) || !touchedTopics.every(isBridgeAutoMemoryTopic)) {
    throw new Error("Malformed workspace memory forget response");
  }
  return {
    ...summary === void 0 ? {} : { summary },
    removedEntries: parsedRemovedEntries,
    touchedTopics
  };
}
__name(parseWorkspaceMemoryForgetResult, "parseWorkspaceMemoryForgetResult");
function parseWorkspaceMemoryDreamResult(response) {
  if (response === null || typeof response !== "object" || Array.isArray(response)) {
    throw new Error("Malformed workspace memory dream response");
  }
  const record = response;
  const summary = record["summary"];
  const touchedTopics = record["touchedTopics"];
  const dedupedEntries = record["dedupedEntries"];
  if (summary !== void 0 && typeof summary !== "string" || !Array.isArray(touchedTopics) || !touchedTopics.every(isBridgeAutoMemoryTopic) || typeof dedupedEntries !== "number" || !Number.isFinite(dedupedEntries)) {
    throw new Error("Malformed workspace memory dream response");
  }
  return {
    ...summary === void 0 ? {} : { summary },
    touchedTopics,
    dedupedEntries
  };
}
__name(parseWorkspaceMemoryDreamResult, "parseWorkspaceMemoryDreamResult");
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) {
  try {
    entry.events.publish({
      type: "turn_complete",
      data: {
        sessionId,
        stopReason: promptResult.stopReason ?? "end_turn",
        ...promptId ? { promptId } : {}
      },
      ...originatorClientId ? { originatorClientId } : {}
    });
  } catch {
  }
}
__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;
  try {
    entry.events.publish({
      type: "turn_error",
      data: {
        sessionId,
        message,
        ...code ? { code } : {},
        ...promptId ? { promptId } : {}
      },
      ...originatorClientId ? { originatorClientId } : {}
    });
  } catch {
  }
}
__name(broadcastTurnError, "broadcastTurnError");
function hasControlCharacter2(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(hasControlCharacter2, "hasControlCharacter");
function extractPromptText(prompt) {
  if (!Array.isArray(prompt)) return "";
  let hasImage = false;
  for (const block of prompt) {
    if (block["type"] === "image") {
      hasImage = true;
    }
    if (block["type"] === "text" && typeof block["text"] === "string" && block["text"].length > 0) {
      return block["text"];
    }
  }
  return hasImage ? "[image]" : "";
}
__name(extractPromptText, "extractPromptText");
var DEFAULT_INIT_TIMEOUT_MS = 1e4;
var PERSIST_TIMEOUT_MS = 5e3;
var MCP_RESTART_TIMEOUT_MS = 3e5;
var WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS = 3e5;
var MCP_OAUTH_TIMEOUT_MS = 6e5;
var DAEMON_RETRY_META_KEY = "qwen.daemon.retry";
var DAEMON_CONTINUE_META_KEY = "qwen.daemon.continueLastTurn";
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 (!path3.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 (hasNoChannelWork(ci)) {
        writeStderrLine(
          `qwen serve: idle timeout (${timeoutMs}ms) expired, killing channel`
        );
        void killChannelWithLog(ci, "idle timeout");
      }
    }, timeoutMs);
    idleTimer.unref();
  }
  __name(startIdleTimer, "startIdleTimer");
  function hasNoChannelWork(ci, opts2) {
    const inFlightSpawnCount = ci.sessionSpawnsInFlight - (opts2?.ignoreCurrentSessionSpawn === true ? 1 : 0);
    const pendingRestoreCount = ci.pendingRestoreIds.size - (opts2?.ignoreRestoreId !== void 0 && ci.pendingRestoreIds.has(opts2.ignoreRestoreId) ? 1 : 0);
    return ci.sessionIds.size === 0 && pendingRestoreCount === 0 && ci.workspaceControlInFlight === 0 && inFlightSpawnCount === 0;
  }
  __name(hasNoChannelWork, "hasNoChannelWork");
  async function reapPendingEmptyChannel(ci) {
    if (!ci.emptyReapPending || !hasNoChannelWork(ci)) return;
    ci.emptyReapPending = false;
    ci.isDying = true;
    await ci.channel.kill().catch(() => {
    });
  }
  __name(reapPendingEmptyChannel, "reapPendingEmptyChannel");
  async function withWorkspaceControl(ci, fn) {
    ci.workspaceControlInFlight++;
    try {
      return await fn();
    } finally {
      ci.workspaceControlInFlight = Math.max(
        0,
        ci.workspaceControlInFlight - 1
      );
      await reapPendingEmptyChannel(ci);
    }
  }
  __name(withWorkspaceControl, "withWorkspaceControl");
  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 toSessionSummary = /* @__PURE__ */ __name((entry) => ({
    sessionId: entry.sessionId,
    workspaceCwd: entry.workspaceCwd,
    createdAt: entry.createdAt,
    displayName: entry.displayName,
    clientCount: entry.clientIds.size,
    hasActivePrompt: entry.promptActive
  }), "toSessionSummary");
  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 sessionIds = /* @__PURE__ */ new Set();
      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
          );
        },
        // Reverse tool channel (issue #5626, Phase 2): forward the optional
        // client-hosted-MCP sender lookup so `BridgeClient.extMethod` can
        // answer `qwen/control/client_mcp/message` from the child by reaching
        // the per-WS-connection `ClientMcpRegistrar`. Omitted callers (tests,
        // Mode A) never host a client MCP server, so the method stays
        // unreachable.
        opts.clientMcpSender,
        (sessionId) => sessionIds.has(sessionId)
      );
      const connection = new ClientSideConnection(() => client, channel.stream);
      const info = {
        channel,
        connection,
        client,
        sessionIds,
        pendingRestoreIds: /* @__PURE__ */ new Set(),
        sessionSpawnsInFlight: 0,
        workspaceControlInFlight: 0,
        emptyReapPending: false,
        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();
    ci.sessionSpawnsInFlight++;
    let sessionRegistered = false;
    let newSessionResp;
    try {
      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 (hasNoChannelWork(ci, { ignoreCurrentSessionSpawn: true })) {
          ci.isDying = true;
          await ci.channel.kill().catch(() => {
          });
        } else {
          ci.emptyReapPending = true;
        }
        throw err;
      }
      if (shuttingDown) {
        throw new Error("AcpSessionBridge is shutting down");
      }
      const entry = createSessionEntry(
        ci,
        newSessionResp.sessionId,
        boundWorkspace
      );
      sessionRegistered = true;
      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
      };
    } finally {
      ci.sessionSpawnsInFlight = Math.max(0, ci.sessionSpawnsInFlight - 1);
      if (!sessionRegistered) {
        await reapPendingEmptyChannel(ci);
      }
    }
  }
  __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 (!path3.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, opts2) => {
    if (!ci || ci.channel !== entry.channel) {
      if (opts2?.throwOnFailure === true) {
        writeStderrLine(
          `qwen serve: ${label} ACP session close channel unavailable for session ${JSON.stringify(entry.sessionId)}; agent close skipped`
        );
        throw new Error(
          `ACP session close channel unavailable for ${entry.sessionId}`
        );
      }
      return;
    }
    try {
      await Promise.race([
        withTimeout(
          entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionClose, {
            sessionId: entry.sessionId,
            ...opts2?.requireFlush === true ? { requireFlush: true } : {}
          }),
          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
        )}`
      );
      if (opts2?.throwOnFailure === true) {
        throw 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,
      artifacts: new SessionArtifactStore({ sessionId, workspaceCwd }),
      promptQueue: Promise.resolve(),
      pendingPromptCount: 0,
      pendingPromptList: [],
      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 publishArtifactChanges = /* @__PURE__ */ __name((entry, changes, originatorClientId) => {
    for (const change of changes) {
      entry.events.publish({
        type: "artifact_changed",
        data: { sessionId: entry.sessionId, change },
        ...originatorClientId ? { originatorClientId } : {}
      });
    }
  }, "publishArtifactChanges");
  const makeClientArtifactInput = /* @__PURE__ */ __name((artifact, clientId) => {
    const input = {
      title: artifact.title,
      kind: artifact.kind,
      storage: artifact.storage,
      description: artifact.description,
      workspacePath: artifact.workspacePath,
      managedId: artifact.managedId,
      url: artifact.url,
      mimeType: artifact.mimeType,
      sizeBytes: artifact.sizeBytes,
      metadata: artifact.metadata,
      source: "client"
    };
    if (clientId) {
      input.clientId = clientId;
    }
    return input;
  }, "makeClientArtifactInput");
  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 ?? {},
        hasActivePrompt: existing.promptActive,
        ...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,
        hasActivePrompt: entry.promptActive
      };
    }
    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);
        }
        ci.emptyReapPending = hasNoChannelWork(ci, {
          ignoreRestoreId: req.sessionId
        });
        if (ci.emptyReapPending) {
          ci.isDying = true;
        }
        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 ?? {},
          hasActivePrompt: racedEntry.promptActive,
          ...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,
        hasActivePrompt: entry.promptActive,
        ...replayFieldsFor(entry, action)
      };
    })().finally(async () => {
      ci?.pendingRestoreIds.delete(req.sessionId);
      ci?.client.clearRestoreInFlight(req.sessionId);
      pendingRestoreEvents.delete(req.sessionId);
      if (!registeredEntry) {
        restoreEvents.close();
        ci?.client.markSessionClosed(req.sessionId);
      }
      if (ci) {
        await reapPendingEmptyChannel(ci);
      }
    });
    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)`
      );
    }
    const requireAgentClose = closeOpts?.requireAgentClose === true;
    if (requireAgentClose) {
      await notifyAgentSessionClose(entry, ci, "closeSession", {
        throwOnFailure: true,
        requireFlush: true
      });
    }
    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();
    if (!requireAgentClose) {
      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 && hasNoChannelWork(ci)) {
      await reapPendingEmptyChannel(ci);
      if (!ci.isDying) {
        await startIdleTimer(ci, `closeSession "${sessionId}"`);
      }
    }
  }
  __name(closeSessionImpl, "closeSessionImpl");
  startSessionReaper();
  const bridgeApi = {
    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,
            hasActivePrompt: existing.promptActive
          };
        }
        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,
            hasActivePrompt: attachedEntry.promptActive
          };
        }
      }
      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 promptId = context?.promptId ?? randomUUID2();
      const isQueued = entry.pendingPromptCount > 1;
      const pendingAbort = new AbortController();
      if (signal) {
        if (signal.aborted) {
          pendingAbort.abort(signal.reason);
        } else {
          signal.addEventListener(
            "abort",
            () => pendingAbort.abort(signal.reason),
            { once: true }
          );
        }
      }
      const pendingEntry = {
        promptId,
        queuedAt,
        ...originatorClientId !== void 0 ? { originatorClientId } : {},
        text: extractPromptText(req.prompt),
        abortController: pendingAbort,
        state: isQueued ? "queued" : "running"
      };
      entry.pendingPromptList.push(pendingEntry);
      if (isQueued) {
        entry.events.publish({
          type: "pending_prompt_added",
          data: {
            sessionId,
            promptId: pendingEntry.promptId,
            text: pendingEntry.text,
            queuedAt: pendingEntry.queuedAt
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
      }
      const result = entry.promptQueue.then(
        () => telemetry.runWithContext(capturedContext, async () => {
          const queueWaitMs = Date.now() - queuedAt;
          telemetry.metrics?.promptQueueWait(queueWaitMs);
          if (pendingAbort.signal.aborted) {
            throw new DOMException("Prompt aborted", "AbortError");
          }
          if (pendingEntry.state === "queued") {
            pendingEntry.state = "running";
            entry.events.publish({
              type: "pending_prompt_started",
              data: {
                sessionId,
                promptId: pendingEntry.promptId,
                text: pendingEntry.text
              },
              ...originatorClientId ? { originatorClientId } : {}
            });
          }
          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
                  }
                );
                assertLivePromptEntry(sessionId, entry);
                const requestedRetry = req.retry === true;
                const isRetry = requestedRetry && entry.retryAllowed;
                entry.retryAllowed = false;
                const isContinue = context?.continue === true;
                const promptRequest = (() => {
                  const copy = {
                    ...normalized
                  };
                  delete copy.retry;
                  const meta = copy._meta && typeof copy._meta === "object" ? { ...copy._meta } : {};
                  delete meta[DAEMON_RETRY_META_KEY];
                  delete meta[DAEMON_CONTINUE_META_KEY];
                  if (isRetry) {
                    meta[DAEMON_RETRY_META_KEY] = true;
                  }
                  if (isContinue) {
                    meta[DAEMON_CONTINUE_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 && !isContinue) {
                    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) => {
                    if (err instanceof DOMException && err.name === "AbortError" && pendingEntry.state === "queued") {
                      writeStderrLine(
                        `sendPrompt: queued prompt removed before agent forward for session ${sessionId}`
                      );
                      return;
                    }
                    writeStderrLine(
                      `sendPrompt: forward failed for session ${sessionId}: ${extractErrorMessage(err)}`
                    );
                    broadcastPromptCancelledOnce(
                      entry,
                      sessionId,
                      originatorClientId,
                      "forward_failed"
                    );
                    cancelPendingForSession(sessionId);
                    entry.connection.cancel({ sessionId }).catch((err2) => {
                      writeStderrLine(
                        `[pending-prompt] cancel forward failed after prompt abort session=${sessionId}: ${extractErrorMessage(err2)}`
                      );
                    });
                  }
                ).catch(() => {
                });
                const abortSignal = pendingAbort.signal;
                const onAbort = /* @__PURE__ */ __name(() => {
                  broadcastPromptCancelledOnce(
                    entry,
                    sessionId,
                    originatorClientId
                  );
                  cancelPendingForSession(sessionId);
                  entry.connection.cancel({ sessionId }).catch((err) => {
                    writeStderrLine(
                      `[pending-prompt] cancel forward failed after removePendingPrompt session=${sessionId}: ${extractErrorMessage(err)}`
                    );
                  });
                }, "onAbort");
                if (abortSignal.aborted) {
                  onAbort();
                } else {
                  abortSignal.addEventListener("abort", onAbort, {
                    once: true
                  });
                  if (abortSignal.aborted) onAbort();
                  racedPromise.finally(
                    () => abortSignal.removeEventListener("abort", onAbort)
                  ).catch(() => {
                  });
                }
                return racedPromise;
              }
            );
          } finally {
            telemetry.metrics?.promptDuration(Date.now() - dispatchStartMs);
          }
        })
      );
      result.then(
        (promptResult) => {
          broadcastTurnComplete(
            entry,
            sessionId,
            promptResult,
            pendingEntry.promptId,
            originatorClientId
          );
        },
        (err) => {
          if (err instanceof DOMException && err.name === "AbortError") return;
          broadcastTurnError(
            entry,
            sessionId,
            err,
            pendingEntry.promptId,
            originatorClientId
          );
        }
      );
      entry.promptQueue = result.then(
        () => void 0,
        () => void 0
      );
      result.finally(() => {
        const listIdx = entry.pendingPromptList.indexOf(pendingEntry);
        if (listIdx !== -1) {
          entry.pendingPromptList.splice(listIdx, 1);
          if (isQueued) {
            try {
              entry.events.publish({
                type: "pending_prompt_completed",
                data: {
                  sessionId,
                  promptId: pendingEntry.promptId,
                  state: "completed"
                },
                ...originatorClientId ? { originatorClientId } : {}
              });
            } catch {
            }
          }
        }
        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 changeSessionCwd(sessionId, req, context) {
      if (shuttingDown) throw new Error("AcpSessionBridge is shutting down");
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const originatorClientId = resolveTrustedClientId(
        entry,
        context?.clientId
      );
      const cdPromise = entry.promptQueue.then(async () => {
        if (entry.promptActive) {
          throw new CdWhilePromptActiveError(sessionId);
        }
        const ci = await ensureChannel();
        const raw = await ci.connection.extMethod(
          SERVE_CONTROL_EXT_METHODS.sessionCd,
          {
            sessionId,
            path: req.path
          }
        );
        const extResult = raw;
        if (typeof extResult?.previousCwd !== "string" || typeof extResult?.newCwd !== "string" || !Array.isArray(extResult?.warnings)) {
          throw new Error(
            `changeSessionCwd: unexpected response shape from agent: ${JSON.stringify(raw)}`
          );
        }
        if (extResult.previousCwd !== extResult.newCwd) {
          entry.events.publish({
            type: "session_cwd_changed",
            data: {
              sessionId,
              previousCwd: extResult.previousCwd,
              newCwd: extResult.newCwd
            },
            ...originatorClientId ? { originatorClientId } : {}
          });
        }
        return extResult;
      });
      entry.promptQueue = cdPromise.then(
        () => void 0,
        () => void 0
      );
      const result = await withTimeout(
        cdPromise,
        Math.max(initTimeoutMs, 3e4),
        "changeSessionCwd"
      );
      writeStderrLine(
        `qwen serve: session ${sessionId} cwd changed: ${result.previousCwd} -> ${result.newCwd}` + (result.warnings.length > 0 ? ` (warnings: ${result.warnings.join("; ")})` : "")
      );
      return { sessionId, ...result };
    },
    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 (hasControlCharacter2(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 };
    },
    async getSessionArtifacts(sessionId) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      return entry.artifacts.list();
    },
    async addSessionArtifact(sessionId, artifact, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const clientId = resolveTrustedClientId(entry, context?.clientId);
      const input = makeClientArtifactInput(artifact, clientId);
      const result = await entry.artifacts.upsertMany([input], { strict: true });
      publishArtifactChanges(entry, result.changes, clientId);
      return result;
    },
    async removeSessionArtifact(sessionId, artifactId, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      const clientId = resolveTrustedClientId(entry, context?.clientId);
      const result = await entry.artifacts.remove(artifactId, { clientId });
      publishArtifactChanges(entry, result.changes, clientId);
      return result;
    },
    listWorkspaceSessions(workspaceCwd) {
      if (!path3.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(toSessionSummary(entry));
        }
      }
      return out;
    },
    getSessionSummary(sessionId) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      return toSessionSummary(entry);
    },
    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 isWorkspaceMemoryRememberAvailable() {
      const info = await ensureChannel();
      try {
        const response = await withWorkspaceControl(
          info,
          () => withTimeout(
            Promise.race([
              info.connection.extMethod(
                SERVE_CONTROL_EXT_METHODS.workspaceMemoryRememberAvailability,
                { cwd: boundWorkspace }
              ),
              getChannelClosedReject(info)
            ]),
            initTimeoutMs,
            SERVE_CONTROL_EXT_METHODS.workspaceMemoryRememberAvailability
          )
        );
        return response !== null && typeof response === "object" && response["available"] === true;
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace memory remember availability");
        }
      }
    },
    async runWorkspaceMemoryRemember(request) {
      const info = await ensureChannel();
      try {
        const response = await withWorkspaceControl(
          info,
          () => withTimeout(
            Promise.race([
              info.connection.extMethod(
                SERVE_CONTROL_EXT_METHODS.workspaceMemoryRemember,
                { ...request, cwd: boundWorkspace }
              ),
              getChannelClosedReject(info)
            ]),
            WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS,
            SERVE_CONTROL_EXT_METHODS.workspaceMemoryRemember
          )
        );
        return parseWorkspaceMemoryRememberResult(response);
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace memory remember");
        }
      }
    },
    async runWorkspaceMemoryForget(request) {
      const info = await ensureChannel();
      try {
        const response = await withWorkspaceControl(
          info,
          () => withTimeout(
            Promise.race([
              info.connection.extMethod(
                SERVE_CONTROL_EXT_METHODS.workspaceMemoryForget,
                { ...request, cwd: boundWorkspace }
              ),
              getChannelClosedReject(info)
            ]),
            WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS,
            SERVE_CONTROL_EXT_METHODS.workspaceMemoryForget
          )
        );
        return parseWorkspaceMemoryForgetResult(response);
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace memory forget");
        }
      }
    },
    async runWorkspaceMemoryDream() {
      const info = await ensureChannel();
      try {
        const response = await withWorkspaceControl(
          info,
          () => withTimeout(
            Promise.race([
              info.connection.extMethod(
                SERVE_CONTROL_EXT_METHODS.workspaceMemoryDream,
                { cwd: boundWorkspace }
              ),
              getChannelClosedReject(info)
            ]),
            WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS,
            SERVE_CONTROL_EXT_METHODS.workspaceMemoryDream
          )
        );
        return parseWorkspaceMemoryDreamResult(response);
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace memory dream");
        }
      }
    },
    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 getWorkspaceMcpResourcesStatus(serverName) {
      return requestWorkspaceStatus(
        SERVE_STATUS_EXT_METHODS.workspaceMcpResources,
        () => ({
          v: STATUS_SCHEMA_VERSION,
          workspaceCwd: boundWorkspace,
          serverName,
          initialized: false,
          acpChannelLive: false,
          resources: [],
          errors: [
            {
              kind: "mcp_resources",
              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 continueSession(sessionId, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      resolveTrustedClientId(entry, context?.clientId);
      const decision = await requestSessionStatus(sessionId, SERVE_CONTROL_EXT_METHODS.sessionContinue);
      if (!decision.accepted) {
        return decision;
      }
      const liveEntry = byId.get(sessionId);
      if (!liveEntry) throw new SessionNotFoundError(sessionId);
      const lastEventId = liveEntry.events.lastEventId;
      const promptId = context?.promptId;
      const promptPromise = bridgeApi.sendPrompt(
        sessionId,
        { sessionId, prompt: [] },
        void 0,
        {
          ...context?.clientId !== void 0 ? { clientId: context.clientId } : {},
          ...promptId !== void 0 ? { promptId } : {},
          continue: true
        }
      );
      promptPromise.catch((err) => {
        teeServeDebugLine(
          `continueSession: continuation turn failed for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`
        );
      });
      return {
        ...decision,
        ...promptId !== void 0 ? { promptId } : {},
        lastEventId
      };
    },
    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
      };
    },
    getPendingPrompts(sessionId, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      resolveTrustedClientId(entry, context?.clientId);
      return entry.pendingPromptList.map((p) => ({
        promptId: p.promptId,
        text: p.text,
        queuedAt: p.queuedAt,
        state: p.state,
        ...p.originatorClientId !== void 0 ? { originatorClientId: p.originatorClientId } : {}
      }));
    },
    removePendingPrompt(sessionId, promptId, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      resolveTrustedClientId(entry, context?.clientId);
      const idx = entry.pendingPromptList.findIndex(
        (p) => p.promptId === promptId
      );
      if (idx === -1) return { removed: false };
      const target = entry.pendingPromptList[idx];
      writeStderrLine(
        `[pending-prompt] session=${sessionId} removing promptId=${promptId} state=${target.state}`
      );
      target.abortController.abort(
        new DOMException("Prompt removed by user", "AbortError")
      );
      entry.pendingPromptList.splice(idx, 1);
      try {
        entry.events.publish({
          type: "pending_prompt_completed",
          data: { sessionId, promptId, state: "removed" },
          ...target.originatorClientId ? { originatorClientId: target.originatorClientId } : {}
        });
      } catch {
      }
      return { removed: true };
    },
    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 && hasNoChannelWork(ci)) {
        await reapPendingEmptyChannel(ci);
        if (!ci.isDying) {
          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 && hasNoChannelWork(ci)) {
        await startIdleTimer(ci);
      }
    }
  };
  return bridgeApi;
}
__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 {
  SessionArtifactValidationError,
  extractErrorMessage,
  extractErrorCode,
  createAcpSessionBridge,
  createHttpAcpBridge
};
/**
 * @license
 * Copyright 2025 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
