// Force strict mode and setup for ESM
"use strict";
import {
  LOAD_REPLAY_BULK_MODE,
  LOAD_REPLAY_META_KEY,
  LOAD_REPLAY_MODE_META_KEY,
  LOAD_REPLAY_VERSION,
  MID_TURN_QUEUE_DRAIN_METHOD
} from "./chunk-PZL23GTM.js";
import {
  DEFAULT_RING_SIZE,
  EVENT_SCHEMA_VERSION,
  EventBus,
  serializedBridgeEventByteLength
} from "./chunk-6RLROE6G.js";
import {
  normalizeCompactedReplayMaxBytes
} from "./chunk-COH43MRJ.js";
import {
  BranchWhilePromptActiveError,
  CancelSentinelCollisionError,
  CdWhilePromptActiveError,
  InvalidClientIdError,
  InvalidPermissionOptionError,
  InvalidRewindTargetError,
  InvalidSessionMetadataError,
  InvalidSessionScopeError,
  MCP_RESTART_SERVER_DEADLINE_MS,
  PermissionForbiddenError,
  PromptQueueFullError,
  RestoreInProgressError,
  SessionBusyError,
  SessionLimitExceededError,
  SessionNotFoundError,
  SessionShellClientRequiredError,
  SessionShellDisabledError,
  WorkspaceMismatchError,
  isNotCurrentlyGeneratingCancelError
} from "./chunk-L4Y55KIS.js";
import {
  defaultSpawnChannelFactory
} from "./chunk-PRERFC23.js";
import {
  BridgeChannelClosedError,
  BridgeTimeoutError,
  SERVE_CONTROL_EXT_METHODS,
  SERVE_STATUS_EXT_METHODS,
  STATUS_SCHEMA_VERSION,
  createIdleWorkspaceExtensionsStatus,
  createIdleWorkspaceHooksStatus
} from "./chunk-LX3DYYB3.js";
import {
  canonicalizeWorkspace
} from "./chunk-W7FJ3N32.js";
import {
  ClientSideConnection,
  PROTOCOL_VERSION,
  RequestError
} from "./chunk-UDXSXLER.js";
import {
  SESSION_ARTIFACT_PERSISTENCE_VERSION,
  ShellExecutionService,
  TrustGateError,
  WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY,
  WORKSPACE_CONTENT_SHA256_METADATA_KEY,
  isPrototypeMetadataKey,
  isReservedWorkspaceMetadataKey,
  metadataBudgetBytes,
  normalizeSnapshotPayload,
  stableSessionArtifactId
} from "./chunk-KN5EBZLT.js";
import {
  DAEMON_TRACEPARENT_META_KEY,
  DAEMON_TRACESTATE_META_KEY
} from "./chunk-D5K5YBSN.js";
import {
  init_esbuild_shims
} from "./chunk-5O2XNYP6.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([
  "history_truncated",
  "slow_client_warning",
  "client_evicted",
  "replay_complete",
  "stream_error"
]);
var LATEST_WINS_UPDATES = /* @__PURE__ */ new Set([
  "available_commands_update",
  "current_mode_update"
]);
var REPLAY_SEGMENT_COMPACT_THRESHOLD = 64;
var TurnBoundaryCompactionEngine = class {
  static {
    __name(this, "TurnBoundaryCompactionEngine");
  }
  maxReplayBytes;
  onReplayWindowEviction;
  replaySegments = [];
  replaySegmentStart = 0;
  replayBytes = 0;
  liveJournal = [];
  lastEventId = 0;
  closed = false;
  truncatedEvents = 0;
  truncatedTurns = 0;
  slots = [];
  toolSlotIndex = /* @__PURE__ */ new Map();
  textSlotIndex = /* @__PURE__ */ new Map();
  constructor(opts = {}) {
    this.maxReplayBytes = normalizeCompactedReplayMaxBytes(opts.maxReplayBytes);
    this.onReplayWindowEviction = opts.onReplayWindowEviction;
  }
  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() {
    const compactedTurns = this.flattenReplaySegments();
    if (this.truncatedEvents > 0) {
      compactedTurns.unshift(
        this.makeHistoryTruncatedEvent(compactedTurns.length)
      );
    }
    return {
      compactedTurns,
      liveJournal: this.liveJournal.slice(),
      lastEventId: this.lastEventId
    };
  }
  seed(snapshot) {
    if (this.closed) return;
    this.resetReplayWindow();
    this.lastEventId = snapshot.lastEventId;
    for (const event of snapshot.compactedTurns) {
      if (TRANSIENT_TYPES.has(event.type)) continue;
      this.addReplaySegment([event], 0);
    }
    this.liveJournal = [];
    this.slots = [];
    this.toolSlotIndex.clear();
    this.textSlotIndex.clear();
  }
  seedReplayEvents(events) {
    if (this.closed) return;
    this.resetReplayWindow();
    for (const event of events) {
      this.recordLastEventId(event);
      if (TRANSIENT_TYPES.has(event.type)) continue;
      this.addReplaySegment([event], 0);
    }
    this.liveJournal = [];
    this.slots = [];
    this.toolSlotIndex.clear();
    this.textSlotIndex.clear();
  }
  close() {
    if (this.closed) return;
    this.closed = true;
    this.resetReplayWindow();
    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.addReplaySegment(compacted, 1);
    this.liveJournal = [];
    this.slots = [];
    this.toolSlotIndex.clear();
    this.textSlotIndex.clear();
  }
  recordLastEventId(event) {
    if (event.id !== void 0) {
      this.lastEventId = event.id;
    }
  }
  addReplaySegment(events, turnCount) {
    if (events.length === 0) return;
    const bytes = events.reduce(
      (sum, event) => sum + serializedBridgeEventByteLength(event),
      0
    );
    this.replaySegments.push({ events: events.slice(), bytes, turnCount });
    this.replayBytes += bytes;
    this.enforceReplayWindow();
  }
  enforceReplayWindow() {
    let droppedSegmentCount = 0;
    let droppedBytes = 0;
    let droppedEvents = 0;
    let droppedTurns = 0;
    while (this.replayBytes > this.maxReplayBytes && this.activeReplaySegmentCount() > 1) {
      const dropped = this.replaySegments[this.replaySegmentStart];
      this.replaySegmentStart += 1;
      droppedSegmentCount += 1;
      droppedBytes += dropped.bytes;
      droppedEvents += dropped.events.length;
      droppedTurns += dropped.turnCount;
      this.replayBytes -= dropped.bytes;
      this.truncatedEvents += dropped.events.length;
      this.truncatedTurns += dropped.turnCount;
    }
    if (droppedSegmentCount > 0) {
      this.compactReplaySegmentQueueIfNeeded();
      this.notifyReplayWindowEviction({
        droppedBytes,
        droppedEvents,
        droppedSegments: droppedSegmentCount,
        droppedTurns,
        maxBytes: this.maxReplayBytes,
        retainedBytes: this.replayBytes,
        retainedEvents: this.flattenReplaySegments().length
      });
    }
  }
  flattenReplaySegments() {
    return this.replaySegments.slice(this.replaySegmentStart).flatMap((segment) => segment.events);
  }
  activeReplaySegmentCount() {
    return this.replaySegments.length - this.replaySegmentStart;
  }
  compactReplaySegmentQueueIfNeeded() {
    if (this.replaySegmentStart < REPLAY_SEGMENT_COMPACT_THRESHOLD) return;
    this.replaySegments.splice(0, this.replaySegmentStart);
    this.replaySegmentStart = 0;
  }
  notifyReplayWindowEviction(eviction) {
    try {
      this.onReplayWindowEviction?.(eviction);
    } catch {
    }
  }
  makeHistoryTruncatedEvent(retainedEvents) {
    return {
      v: EVENT_SCHEMA_VERSION,
      type: "history_truncated",
      data: {
        reason: "replay_window_exceeded",
        truncatedEvents: this.truncatedEvents,
        retainedEvents,
        maxBytes: this.maxReplayBytes,
        ...this.truncatedTurns > 0 ? { truncatedTurns: this.truncatedTurns } : {},
        fullTranscriptAvailable: true
      }
    };
  }
  resetReplayWindow() {
    this.replaySegments = [];
    this.replaySegmentStart = 0;
    this.replayBytes = 0;
    this.truncatedEvents = 0;
    this.truncatedTurns = 0;
  }
};
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/session-source.ts
init_esbuild_shims();
var SESSION_SOURCE_TYPE_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
var MAX_SESSION_SOURCE_ID_LENGTH = 256;
function parseSessionSource(sourceType, sourceId) {
  if (sourceType === void 0 && sourceId === void 0) return {};
  if (typeof sourceType !== "string" || !SESSION_SOURCE_TYPE_PATTERN.test(sourceType)) {
    return {
      error: "`sourceType` must match [a-z][a-z0-9_-]{0,63} when provided"
    };
  }
  if (sourceId === void 0) return { sourceType };
  if (typeof sourceId !== "string" || sourceId.length === 0 || sourceId.length > MAX_SESSION_SOURCE_ID_LENGTH || [...sourceId].some((character) => {
    const code = character.charCodeAt(0);
    return code <= 31 || code === 127;
  })) {
    return {
      error: `\`sourceId\` must be a non-empty string of at most ${MAX_SESSION_SOURCE_ID_LENGTH} characters without control characters`
    };
  }
  return { sourceType, sourceId };
}
__name(parseSessionSource, "parseSessionSource");

// 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/bridgeOptions.ts
init_esbuild_shims();
var MAX_SUB_SESSION_PROMPT_CHARS = 1e5;
var MAX_SUB_SESSION_NAME_CHARS = 200;

// 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 pendingInteractionOptions(options) {
  return options.map((option) => ({
    optionId: String(option.optionId ?? ""),
    ...typeof option.name === "string" ? { label: option.name } : {},
    ...typeof option.kind === "string" ? { kind: option.kind } : {}
  }));
}
__name(pendingInteractionOptions, "pendingInteractionOptions");
function pendingInteractionFromRequest(requestId, params) {
  const toolCall = params.toolCall;
  const meta = isRecord(toolCall["_meta"]) ? toolCall["_meta"] : void 0;
  const rawInput = toolCall["rawInput"];
  const options = pendingInteractionOptions(
    Array.isArray(params.options) ? params.options : []
  );
  const isUserQuestion = meta?.["qwenInteractionKind"] === "user_question";
  if (isUserQuestion) {
    const rawQuestions = Array.isArray(meta?.["qwenQuestions"]) ? meta["qwenQuestions"] : isRecord(rawInput) && Array.isArray(rawInput["questions"]) ? rawInput["questions"] : [];
    return {
      requestId,
      kind: "user_question",
      createdAt: (/* @__PURE__ */ new Date()).toISOString(),
      ...typeof toolCall["title"] === "string" ? { title: toolCall["title"] } : {},
      questions: rawQuestions.flatMap(
        (question, index) => isRecord(question) ? [{ ...question, answerKey: String(index) }] : []
      ),
      options
    };
  }
  return {
    requestId,
    kind: "permission",
    createdAt: (/* @__PURE__ */ new Date()).toISOString(),
    action: {
      ...typeof toolCall["kind"] === "string" ? { type: toolCall["kind"] } : {},
      ...typeof toolCall["title"] === "string" ? { title: toolCall["title"] } : {},
      ...toolCall["content"] !== void 0 ? { content: toolCall["content"] } : {},
      ...toolCall["locations"] !== void 0 ? { locations: toolCall["locations"] } : {},
      ...rawInput !== void 0 ? { input: rawInput } : {}
    },
    options
  };
}
__name(pendingInteractionFromRequest, "pendingInteractionFromRequest");
function fallbackPendingPermissionInteraction(requestId, options) {
  return {
    requestId,
    kind: "permission",
    createdAt: (/* @__PURE__ */ new Date()).toISOString(),
    action: {},
    options: pendingInteractionOptions(options)
  };
}
__name(fallbackPendingPermissionInteraction, "fallbackPendingPermissionInteraction");
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"],
    retention: artifact["retention"]
  };
}
__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)) {
    const code = err.kind === "parse_error" ? -32602 : -32603;
    throw new RequestError(code, 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, onTokenUsage, onCreateSubSession, onGenerationEvent) {
    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;
    this.onTokenUsage = onTokenUsage;
    this.onCreateSubSession = onCreateSubSession;
    this.onGenerationEvent = onGenerationEvent;
  }
  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 options = Array.isArray(params.options) ? params.options : [];
    const allowedOptionIds = new Set(
      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
      },
      ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
    });
    if (!published) return { outcome: { outcome: "cancelled" } };
    let interaction;
    try {
      interaction = pendingInteractionFromRequest(requestId, {
        ...params,
        options
      });
    } catch (error) {
      writeStderrLine(
        `qwen serve: failed to snapshot pending interaction ${requestId}: ${error instanceof Error ? error.message : String(error)}`
      );
      interaction = fallbackPendingPermissionInteraction(requestId, options);
    }
    entry.pendingPermissionIds.add(requestId);
    if (interaction) entry.pendingInteractions.set(requestId, interaction);
    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);
      entry.pendingInteractions.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 prepared = this.prepareSessionUpdateFrames(params, entry);
    for (const frame of prepared.frames) {
      events.publish(frame);
    }
    try {
      this.recordLiveTokenUsage(params, entry);
    } catch {
    }
    if (entry && prepared.artifacts.length > 0) {
      await this.upsertAndPublishArtifacts(entry, prepared.artifacts, {
        trustedPublisher: prepared.trustedPublisher
      });
    }
  }
  prepareSessionUpdateFrames(params, entry) {
    const originator = entry?.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {};
    const frames = [];
    const a2ui = extractA2uiToolUpdate(params);
    if (a2ui) {
      for (const surface of a2ui.surfaces) {
        frames.push({
          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);
    frames.push({
      type: "session_update",
      data: publishParams,
      ...originator,
      ...serverTimestamp !== void 0 ? { _meta: { serverTimestamp } } : {}
    });
    return {
      frames,
      artifacts,
      trustedPublisher: isTrustedArtifactToolUpdate(params, updateMeta)
    };
  }
  async seedSessionUpdates(entry, updates, options = {}) {
    const frames = [];
    const artifactBatches = [];
    for (const update of updates) {
      const prepared = this.prepareSessionUpdateFrames(
        { sessionId: entry.sessionId, update },
        entry
      );
      frames.push(...prepared.frames);
      if (options.ingestArtifacts !== false && prepared.artifacts.length > 0) {
        artifactBatches.push({
          artifacts: prepared.artifacts,
          trustedPublisher: prepared.trustedPublisher
        });
      }
    }
    entry.events.seedReplayEvents(frames);
    for (const batch of artifactBatches) {
      await this.upsertAndPublishArtifacts(entry, batch.artifacts, {
        trustedPublisher: batch.trustedPublisher
      });
    }
  }
  /**
   * Daemon token-burn accounting for LIVE model turns. Called only from
   * `sessionUpdate` (the live session/update fan-in), never from
   * `seedSessionUpdates` — so batch load-replay never lands historical usage in
   * the current metrics window. Additionally guarded on a live `entry`: a stray
   * pending-restore frame (entry not yet registered) is skipped too, so replayed
   * history can't post a phantom burn spike with no model call.
   *
   * Usage rides an otherwise-empty `agent_message_chunk` as `update._meta.usage`
   * with per-round camelCase increments; subagent frames carry their own usage
   * (tagged `parentToolCallId`) and are independent turns, so counting each
   * frame once is the correct total. `_meta`/`usage` are optional and untyped.
   */
  recordLiveTokenUsage(params, entry) {
    if (!this.onTokenUsage || !entry) return;
    const updateMeta = params.update._meta;
    const usage = updateMeta?.["usage"];
    if (usage === null || typeof usage !== "object") return;
    const inputTokens = usage.inputTokens;
    const outputTokens = usage.outputTokens;
    if (typeof inputTokens !== "number" && typeof outputTokens !== "number") {
      return;
    }
    const durationMs = updateMeta?.["durationMs"];
    const apiErrors = updateMeta?.["apiErrors"];
    const apiRetries = updateMeta?.["apiRetries"];
    this.onTokenUsage(
      typeof inputTokens === "number" ? inputTokens : 0,
      typeof outputTokens === "number" ? outputTokens : 0,
      typeof durationMs === "number" ? durationMs : void 0,
      typeof apiErrors === "number" ? apiErrors : 0,
      typeof apiRetries === "number" ? apiRetries : 0
    );
  }
  /**
   * Bounded early-event buffer. The map is scoped to this BridgeClient and
   * therefore to one channel; a stale channel cannot seed a fresh channel's
   * future session. 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 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`). Served methods:
   * `qwen/control/client_mcp/message` (reverse tool channel),
   * `qwen/control/create-sub-session` (the `create_sub_session` tool → daemon
   * spawns a sub-session and, for `'first-turn'`, returns its first-turn
   * result), and `craft/drainMidTurnQueue`: the ACP child calls the last one
   * 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 === SERVE_CONTROL_EXT_METHODS.createSubSession) {
      return this.handleCreateSubSession(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 the `create_sub_session` tool's request: validate, then forward to
   * the daemon-host `onCreateSubSession` callback (which spawns a fresh
   * top-level sub-session and, for `'first-turn'`, waits for its first turn and
   * returns the result). No host wired → `methodNotFound`, which the tool
   * surfaces as "daemon-only".
   */
  async handleCreateSubSession(params) {
    if (!this.onCreateSubSession) {
      throw RequestError.methodNotFound(
        SERVE_CONTROL_EXT_METHODS.createSubSession
      );
    }
    const prompt = params["prompt"];
    if (typeof prompt !== "string" || prompt.length === 0) {
      throw RequestError.invalidParams(
        void 0,
        "`prompt` must be a non-empty string"
      );
    }
    if (prompt.length > MAX_SUB_SESSION_PROMPT_CHARS) {
      throw RequestError.invalidParams(
        void 0,
        `\`prompt\` exceeds the ${MAX_SUB_SESSION_PROMPT_CHARS}-character limit`
      );
    }
    const completion = params["completion"];
    if (completion !== "sent" && completion !== "first-turn") {
      throw RequestError.invalidParams(
        void 0,
        "`completion` must be 'sent' or 'first-turn'"
      );
    }
    const name = params["name"];
    if (typeof name === "string" && name.length > MAX_SUB_SESSION_NAME_CHARS) {
      throw RequestError.invalidParams(
        void 0,
        `\`name\` exceeds the ${MAX_SUB_SESSION_NAME_CHARS}-character limit`
      );
    }
    const callerSessionId = params["callerSessionId"];
    if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId)) {
      throw RequestError.invalidParams(
        void 0,
        "`callerSessionId` is required and must name a session owned by this connection"
      );
    }
    const model = params["model"];
    const result = await this.onCreateSubSession({
      prompt,
      completion,
      ...typeof model === "string" && model.length > 0 && model.length <= 128 ? { model } : {},
      ...typeof name === "string" && name.length > 0 ? { name } : {},
      callerSessionId
    });
    return {
      sessionId: result.sessionId,
      ...result.result !== void 0 ? { result: result.result } : {},
      ...result.stopReason !== void 0 ? { stopReason: result.stopReason } : {},
      ...result.parentSessionPersisted !== void 0 ? { parentSessionPersisted: result.parentSessionPersisted } : {}
    };
  }
  /**
   * 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/recording-degraded`,
   * `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/generation/event") {
      const sessionId2 = params["sessionId"];
      const requestId = params["requestId"];
      const event = params["event"];
      if (params["v"] !== 1 || typeof sessionId2 !== "string" || typeof requestId !== "string" || !event || typeof event !== "object" || Array.isArray(event)) {
        return;
      }
      const record = event;
      if (record["type"] === "started") {
        const model = record["model"];
        const modelSource = record["modelSource"];
        if (typeof model !== "string" || modelSource !== "fast" && modelSource !== "main") {
          return;
        }
        this.onGenerationEvent?.(sessionId2, {
          type: "started",
          requestId,
          model,
          modelSource
        });
        return;
      }
      if (record["type"] === "thinking") {
        this.onGenerationEvent?.(sessionId2, {
          type: "thinking",
          requestId
        });
        return;
      }
      if (record["type"] === "delta") {
        const seq = record["seq"];
        const text = record["text"];
        if (typeof seq !== "number" || !Number.isSafeInteger(seq) || seq < 0 || typeof text !== "string" || text.length === 0) {
          return;
        }
        this.onGenerationEvent?.(sessionId2, {
          type: "delta",
          requestId,
          seq,
          text
        });
        return;
      }
      return;
    }
    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/recording-degraded") {
      const sessionId2 = params["sessionId"];
      if (params["v"] !== 1 || typeof sessionId2 !== "string" || sessionId2.length === 0 || params["reason"] !== "write_failed") {
        writeStderrLine(
          `[demux] session=${typeof sessionId2 === "string" ? sessionId2 : "<missing>"} type=session_recording_degraded action=dropped reason=malformed`
        );
        return;
      }
      const entry = this.resolveEntry(sessionId2);
      if (entry && !this.ownsSession(sessionId2)) {
        writeStderrLine(
          `[demux] session=${sessionId2} type=session_recording_degraded action=dropped reason=session_not_owned`
        );
        return;
      }
      if (entry) entry.recordingDegraded = true;
      this.publishExtNotification(sessionId2, "session_recording_degraded", {
        sessionId: sessionId2,
        reason: "write_failed"
      });
      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 early 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 early 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 early 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
   * early 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) {
      if (frame.type === "session_recording_degraded") {
        entry.recordingDegraded = true;
      }
      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: "" };
    }
    if (typeof params.limit === "number" && params.limit > 0 && !Number.isSafeInteger(params.limit)) {
      throw RequestError.invalidParams(
        void 0,
        `\`limit\` must be a positive integer, got ${params.limit}`
      );
    }
    if (typeof params.line === "number" && params.line > 0 && !Number.isSafeInteger(params.line)) {
      throw RequestError.invalidParams(
        void 0,
        `\`line\` must be a positive integer, got ${params.line}`
      );
    }
    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/generation-stream.ts
init_esbuild_shims();
var GenerationStreamQueue = class {
  constructor(capacity) {
    this.capacity = capacity;
  }
  static {
    __name(this, "GenerationStreamQueue");
  }
  values = [];
  waiter;
  closed = false;
  failure;
  push(value) {
    if (this.closed) return false;
    if (this.waiter) {
      const waiter = this.waiter;
      this.waiter = void 0;
      waiter.resolve({ value, done: false });
      return true;
    }
    if (this.values.length >= this.capacity) return false;
    this.values.push(value);
    return true;
  }
  close() {
    if (this.closed) return;
    this.closed = true;
    this.settleWaiter();
  }
  fail(error) {
    if (this.closed) return;
    this.failure = error;
    this.closed = true;
    this.settleWaiter();
  }
  settleWaiter() {
    if (!this.waiter) return;
    const waiter = this.waiter;
    this.waiter = void 0;
    if (this.failure !== void 0) waiter.reject(this.failure);
    else waiter.resolve({ value: void 0, done: true });
  }
  next() {
    if (this.values.length > 0) {
      return Promise.resolve({ value: this.values.shift(), done: false });
    }
    if (this.failure !== void 0) return Promise.reject(this.failure);
    if (this.closed) {
      return Promise.resolve({ value: void 0, done: true });
    }
    if (this.waiter) {
      return Promise.reject(
        new Error("GenerationStreamQueue supports only one pending reader")
      );
    }
    return new Promise((resolve2, reject) => {
      this.waiter = { resolve: resolve2, reject };
    });
  }
  [Symbol.asyncIterator]() {
    return {
      next: /* @__PURE__ */ __name(() => this.next(), "next"),
      return: /* @__PURE__ */ __name(async () => {
        this.close();
        return { value: void 0, done: true };
      }, "return")
    };
  }
};

// packages/acp-bridge/src/sessionArtifacts.ts
init_esbuild_shims();
import { createHash } from "node:crypto";
import { constants as fsConstants, 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 SNAPSHOT_AFTER_DURABLE_EVENTS = 50;
var MAX_SNAPSHOT_BACKOFF_MULTIPLIER = 4;
var MAX_TOMBSTONED_IDS = 500;
var MAX_STICKY_EPHEMERAL_IDS = 500;
var MAX_WORKSPACE_HASH_BYTES = 100 * 1024 * 1024;
var SECRET_TOKEN_VALUE_PATTERN = /(?:^|\s)(?:bearer\s+\S{8,}|sk-[A-Za-z0-9_-]{12,}|(?:gh[pousr]|github_pat)_[A-Za-z0-9_/-]{12,}|[a-f0-9]{40,}|[A-Za-z0-9+/]{48,}={0,2})(?:$|\s)/i;
var RESTORE_FAILED_WARNING_PREFIX = "artifact snapshot restore failed";
var RESTORE_PARTIAL_FAILED_WARNING_PREFIX = "artifact snapshot restore partially failed";
function isArtifactRestoreFailureWarning(warning) {
  return warning.startsWith(RESTORE_FAILED_WARNING_PREFIX) || warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX);
}
__name(isArtifactRestoreFailureWarning, "isArtifactRestoreFailureWarning");
var SessionArtifactValidationError = class extends Error {
  constructor(message, field) {
    super(message);
    this.field = field;
    this.name = "SessionArtifactValidationError";
  }
  static {
    __name(this, "SessionArtifactValidationError");
  }
  code = "VALIDATION_FAILED";
};
var SessionArtifactAuthorizationError = class extends Error {
  constructor(sessionId, artifactId, ownerClientId, requesterClientId) {
    super(`artifact ${artifactId} is owned by a different client`);
    this.sessionId = sessionId;
    this.artifactId = artifactId;
    this.ownerClientId = ownerClientId;
    this.requesterClientId = requesterClientId;
    this.name = "SessionArtifactAuthorizationError";
  }
  static {
    __name(this, "SessionArtifactAuthorizationError");
  }
  code = "SESSION_ARTIFACT_FORBIDDEN";
};
var SessionArtifactStore = class {
  static {
    __name(this, "SessionArtifactStore");
  }
  sessionId;
  workspaceCwd;
  maxArtifacts;
  persistence;
  artifacts = /* @__PURE__ */ new Map();
  receivedSeq = 0;
  insertSeq = 0;
  persistenceSeq = 0;
  durableEventsSinceSnapshot = 0;
  consecutiveSnapshotFailures = 0;
  realWorkspaceCwdPromise;
  operationQueue = Promise.resolve();
  tombstonedIds = /* @__PURE__ */ new Set();
  tombstonedClientIds = /* @__PURE__ */ new Map();
  stickyEphemeralIds = /* @__PURE__ */ new Set();
  markerArtifacts = /* @__PURE__ */ new Map();
  lastRestoreWarnings = [];
  lastRestoreWarningDetails = [];
  constructor(options) {
    this.sessionId = options.sessionId;
    this.workspaceCwd = options.workspaceCwd;
    this.maxArtifacts = options.maxArtifacts ?? 200;
    this.persistence = options.persistence;
  }
  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 },
        ...this.lastRestoreWarnings.length > 0 ? { warnings: [...this.lastRestoreWarnings] } : {},
        ...this.lastRestoreWarningDetails.length > 0 ? { warningDetails: [...this.lastRestoreWarningDetails] } : {}
      };
    });
  }
  async get(artifactId) {
    return this.enqueue(async () => {
      const artifact = this.artifacts.get(artifactId);
      if (!artifact) return void 0;
      if (artifact.workspacePath && shouldRefreshWorkspaceStatus(artifact, Date.now())) {
        await this.refreshWorkspaceStatus(artifact, { onError: "missing" });
      }
      return toPublicArtifact(artifact);
    });
  }
  async upsertMany(inputs, options = {}) {
    return this.enqueue(async () => {
      const validationStrict = options.validationStrict ?? options.strict;
      const persistenceStrict = options.persistenceStrict ?? options.strict;
      const before = this.cloneState();
      const normalizedResults = [];
      const warnings = [];
      const warningDetails = [];
      for (const input of inputs) {
        try {
          normalizedResults.push(
            await this.normalizeInput(
              input,
              ++this.receivedSeq,
              options.trustedPublisher === true
            )
          );
        } catch (error) {
          if (validationStrict) {
            throw error;
          }
          const message = error instanceof Error ? error.message : String(error);
          writeStderrLine(
            `[artifacts] session=${this.sessionId} action=dropped reason=${JSON.stringify(
              message
            )}`
          );
        }
      }
      const changes = [];
      try {
        for (const normalized of coalesceByIdentity(normalizedResults)) {
          const artifact = this.applyStickyEphemeralOverride(normalized);
          if (this.shouldSuppressTombstonedUpsert(artifact)) {
            writeStderrLine(
              `[artifacts] session=${this.sessionId} action=tombstone_replay_suppressed artifactId=${artifact.id}`
            );
            continue;
          }
          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;
          }
          try {
            this.denyCrossClientMutation("upsert", existing.id, existing, {
              clientId: artifact.clientId
            });
          } catch (error) {
            if (validationStrict) {
              throw error;
            }
            if (error instanceof SessionArtifactAuthorizationError) {
              warnings.push(error.message);
              continue;
            }
            throw error;
          }
          const updated = mergeArtifact(existing, artifact);
          if (updated.changed) {
            if (updated.artifact.id !== existing.id) {
              const removeChange = {
                action: "removed",
                artifactId: existing.id,
                artifact: toPublicArtifact(existing),
                reason: "explicit",
                durableTombstoneRequired: existing.durableTombstoneRequired || existing.persistedAt !== void 0,
                removedClientId: existing.clientId
              };
              changes.push(removeChange);
              this.artifacts.delete(existing.id);
            } else if (shouldRecordEphemeralUnpin(existing, artifact)) {
              changes.push({
                action: "removed",
                artifactId: existing.id,
                artifact: toPublicArtifact(existing),
                reason: "unpin_to_ephemeral",
                durableTombstoneRequired: true
              });
            }
            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, persistenceStrict)
        );
        const hasStrictDurableTransition = changes.some(
          shouldCommitBeforeDurablePersistence
        );
        let persistenceWarnings;
        if (hasStrictDurableTransition && !persistenceStrict) {
          try {
            persistenceWarnings = await this.persistChanges(changes, true);
          } catch (error) {
            this.restoreState(before);
            const artifactIds = changes.filter(shouldCommitBeforeDurablePersistence).map((change) => change.artifactId);
            const warning = "artifact durable removal not persisted; live changes rolled back";
            writeStderrLine(
              `[artifacts] session=${this.sessionId} action=upsert_rollback warning=${JSON.stringify(
                warning
              )} artifactIds=${JSON.stringify(artifactIds)} error=${JSON.stringify(
                error instanceof Error ? error.message : String(error)
              )}`
            );
            return {
              v: 1,
              sessionId: this.sessionId,
              changes: [],
              warnings: [warning],
              warningDetails: [
                persistenceFailureDetail({
                  message: warning,
                  operation: "upsert",
                  artifactIds,
                  error
                })
              ]
            };
          }
        } else {
          persistenceWarnings = await this.persistChanges(
            changes,
            persistenceStrict
          );
        }
        warnings.push(...persistenceWarnings);
        warningDetails.push(
          ...detailsForPersistenceWarnings(
            persistenceWarnings,
            changes,
            "upsert"
          )
        );
        stripDurableTombstoneMarkers(changes);
      } catch (error) {
        if (validationStrict || persistenceStrict || error instanceof SessionArtifactAuthorizationError) {
          this.restoreState(before);
        }
        throw error;
      }
      return {
        v: 1,
        sessionId: this.sessionId,
        changes,
        ...warnings.length > 0 ? { warnings } : {},
        ...warningDetails.length > 0 ? { warningDetails } : {}
      };
    });
  }
  findPublishedUpgradeTarget(artifact) {
    if (artifact.storage !== "published" || !artifact.trustedPublisher || !artifact.managedId || !artifact.url) {
      return void 0;
    }
    const byUrl = this.artifacts.get(
      stableSessionArtifactId(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: [] };
      }
      this.denyCrossClientMutation("remove", artifactId, existing, options);
      const removeChange = {
        action: "removed",
        artifactId,
        artifact: toPublicArtifact(existing),
        reason: "explicit",
        durableTombstoneRequired: existing.durableTombstoneRequired || existing.retention !== "ephemeral" ? true : void 0,
        removedClientId: existing.clientId
      };
      const changes = [removeChange];
      const needsDurableTombstone = removeChange.durableTombstoneRequired === true;
      if (needsDurableTombstone) {
        const before = this.cloneState();
        this.artifacts.delete(artifactId);
        try {
          await this.persistChanges(changes, true);
        } catch (error) {
          this.restoreState(before);
          const warning = "artifact removal not persisted; live artifact kept";
          return {
            v: 1,
            sessionId: this.sessionId,
            changes: [],
            warnings: [warning],
            warningDetails: [
              persistenceFailureDetail({
                message: warning,
                operation: "remove",
                artifactIds: [artifactId],
                error
              })
            ]
          };
        }
      }
      if (!needsDurableTombstone) {
        this.artifacts.delete(artifactId);
      }
      const warnings = needsDurableTombstone ? [] : await this.persistChanges(changes, false);
      stripDurableTombstoneMarkers(changes);
      const warningDetails = detailsForPersistenceWarnings(
        warnings,
        changes,
        "remove"
      );
      return {
        v: 1,
        sessionId: this.sessionId,
        changes,
        ...warnings.length > 0 ? { warnings } : {},
        ...warningDetails.length > 0 ? { warningDetails } : {}
      };
    });
  }
  async restore(snapshot, options = {}) {
    if (!snapshot) return [];
    return this.enqueue(async () => {
      const warnings = [...snapshot?.warnings ?? []];
      const baselineWarnings = [...warnings];
      const previousState = this.cloneState();
      const preservedLiveEphemeralArtifacts = options.preserveLiveEphemeral ? Array.from(this.artifacts.values()).filter((artifact) => artifact.retention === "ephemeral").map(cloneStoredArtifact) : [];
      let restoredCount = 0;
      this.artifacts.clear();
      this.tombstonedIds.clear();
      this.tombstonedClientIds.clear();
      this.stickyEphemeralIds.clear();
      this.markerArtifacts.clear();
      if (snapshot) {
        this.insertSeq = 0;
        this.persistenceSeq = snapshot.sequence;
        this.durableEventsSinceSnapshot = 0;
        this.consecutiveSnapshotFailures = 0;
        for (const id of snapshot.tombstonedIds) {
          this.tombstonedIds.add(id);
        }
        for (const id of snapshot.stickyEphemeralIds.slice(
          -MAX_STICKY_EPHEMERAL_IDS
        )) {
          this.stickyEphemeralIds.add(id);
        }
        const markerIds = /* @__PURE__ */ new Set([
          ...this.tombstonedIds,
          ...this.stickyEphemeralIds
        ]);
        for (const artifact of snapshot.markerArtifacts ?? []) {
          if (!markerIds.has(artifact.id)) continue;
          const markerArtifact = await this.normalizeRestoredMarkerArtifact(
            artifact,
            warnings
          );
          if (markerArtifact)
            this.markerArtifacts.set(artifact.id, markerArtifact);
        }
      }
      for (const artifact of snapshot?.artifacts ?? []) {
        try {
          const input = persistedArtifactToInput(artifact);
          if (input.retention === "pinned") {
            input.retention = "restorable";
            warnings.push(
              `pinned artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
            );
          }
          let normalized = await this.normalizeInput(
            input,
            ++this.receivedSeq,
            artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
            {
              metadataBudget: "persisted",
              workspaceExpected: workspaceExpectedFromArtifact(artifact),
              hashWorkspaceContent: false
            }
          );
          if (this.stickyEphemeralIds.has(normalized.id) && normalized.retention !== "ephemeral") {
            normalized = {
              ...normalized,
              retention: "ephemeral",
              persistenceWarning: "sticky_override_active"
            };
          }
          const stickyApplied = normalized.persistenceWarning === "sticky_override_active";
          if (normalized.id !== artifact.id) {
            warnings.push(`skipped artifact with mismatched id ${artifact.id}`);
            continue;
          }
          const retention = normalized.retention;
          let persistenceWarning;
          if (stickyApplied) {
            persistenceWarning = "sticky_override_active";
          } else if (normalized.status !== "available") {
            persistenceWarning = "metadata_only_restore";
          }
          const stored = {
            ...normalized,
            retention,
            clientRetained: artifact.clientRetained,
            createdAt: artifact.createdAt,
            updatedAt: artifact.updatedAt,
            persistedAt: artifact.persistedAt,
            status: normalized.status,
            restoreState: "restored",
            persistenceWarning,
            durableTombstoneRequired: retention !== "ephemeral" ? true : void 0,
            insertSeq: ++this.insertSeq
          };
          this.artifacts.set(stored.id, stored);
          restoredCount++;
        } catch (error) {
          warnings.push(
            `skipped artifact restore: ${error instanceof Error ? error.message : String(error)}`
          );
        }
      }
      if (snapshot.artifacts.length > 0 && restoredCount === 0) {
        this.restoreState(previousState);
        const rollbackWarnings = [
          ...baselineWarnings,
          `${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts`
        ];
        this.setLastRestoreWarnings(rollbackWarnings);
        return rollbackWarnings;
      }
      if (previousState.artifacts.size > 0 && baselineWarnings.some(isArtifactSnapshotCompletenessWarning)) {
        this.restoreState(previousState);
        const rollbackWarnings = [
          ...baselineWarnings,
          restoredCount === 0 ? `${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts` : `${RESTORE_PARTIAL_FAILED_WARNING_PREFIX}; kept existing live artifacts`
        ];
        this.setLastRestoreWarnings(rollbackWarnings);
        return rollbackWarnings;
      }
      for (const artifact of preservedLiveEphemeralArtifacts) {
        if (this.artifacts.has(artifact.id) || this.tombstonedIds.has(artifact.id)) {
          continue;
        }
        this.artifacts.set(artifact.id, {
          ...artifact,
          insertSeq: ++this.insertSeq
        });
      }
      const evicted = await this.evictOverflow(/* @__PURE__ */ new Set(), []);
      if (evicted.length > 0) {
        warnings.push("restored artifact list pruned to live limit");
        warnings.push(...await this.persistChanges(evicted, false));
      }
      this.setLastRestoreWarnings(warnings);
      return warnings;
    });
  }
  async recordSnapshot() {
    const persistence = this.persistence;
    if (!persistence) {
      return [
        "artifact persistence unavailable; restored artifacts not snapshotted"
      ];
    }
    return this.enqueue(async () => {
      const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
      const sequence = this.persistenceSeq + 1;
      try {
        await persistence.recordSnapshot(
          this.buildSnapshotPayload(recordedAt, sequence)
        );
        this.persistenceSeq = sequence;
        this.durableEventsSinceSnapshot = 0;
        this.consecutiveSnapshotFailures = 0;
        return [];
      } catch (error) {
        writeStderrLine(
          `[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
            error instanceof Error ? error.message : String(error)
          )}`
        );
        return ["artifact snapshot not persisted"];
      }
    });
  }
  async normalizeRestoredMarkerArtifact(artifact, warnings) {
    try {
      const input = persistedArtifactToInput(artifact);
      if (input.retention === "pinned") {
        input.retention = "restorable";
        warnings.push(
          `pinned marker artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
        );
      }
      const normalized = await this.normalizeInput(
        input,
        ++this.receivedSeq,
        artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
        {
          metadataBudget: "persisted",
          workspaceExpected: workspaceExpectedFromArtifact(artifact),
          hashWorkspaceContent: false
        }
      );
      if (normalized.id !== artifact.id) {
        warnings.push(
          `skipped marker artifact with mismatched id ${artifact.id}`
        );
        return void 0;
      }
      return toPersistedArtifact(
        {
          ...normalized,
          clientRetained: artifact.clientRetained,
          createdAt: artifact.createdAt,
          updatedAt: artifact.updatedAt,
          persistedAt: artifact.persistedAt
        },
        artifact.persistedAt ?? artifact.updatedAt
      );
    } catch (error) {
      warnings.push(
        `skipped marker artifact ${artifact.id}: ${error instanceof Error ? error.message : String(error)}`
      );
      return void 0;
    }
  }
  cloneState() {
    return {
      artifacts: new Map(
        Array.from(this.artifacts.entries()).map(([id, artifact]) => [
          id,
          cloneStoredArtifact(artifact)
        ])
      ),
      receivedSeq: this.receivedSeq,
      insertSeq: this.insertSeq,
      persistenceSeq: this.persistenceSeq,
      durableEventsSinceSnapshot: this.durableEventsSinceSnapshot,
      consecutiveSnapshotFailures: this.consecutiveSnapshotFailures,
      tombstonedIds: new Set(this.tombstonedIds),
      tombstonedClientIds: new Map(this.tombstonedClientIds),
      stickyEphemeralIds: new Set(this.stickyEphemeralIds),
      markerArtifacts: new Map(this.markerArtifacts),
      lastRestoreWarnings: [...this.lastRestoreWarnings],
      lastRestoreWarningDetails: [...this.lastRestoreWarningDetails]
    };
  }
  restoreState(state) {
    this.artifacts.clear();
    for (const [id, artifact] of state.artifacts) {
      this.artifacts.set(id, cloneStoredArtifact(artifact));
    }
    this.receivedSeq = state.receivedSeq;
    this.insertSeq = state.insertSeq;
    this.persistenceSeq = state.persistenceSeq;
    this.durableEventsSinceSnapshot = state.durableEventsSinceSnapshot;
    this.consecutiveSnapshotFailures = state.consecutiveSnapshotFailures;
    this.tombstonedIds.clear();
    for (const id of state.tombstonedIds) {
      this.tombstonedIds.add(id);
    }
    this.tombstonedClientIds.clear();
    for (const [id, clientId] of state.tombstonedClientIds) {
      this.tombstonedClientIds.set(id, clientId);
    }
    this.stickyEphemeralIds.clear();
    for (const id of state.stickyEphemeralIds) {
      this.stickyEphemeralIds.add(id);
    }
    this.markerArtifacts.clear();
    for (const [id, artifact] of state.markerArtifacts) {
      this.markerArtifacts.set(id, artifact);
    }
    this.setLastRestoreWarnings(state.lastRestoreWarnings);
    this.lastRestoreWarningDetails = [...state.lastRestoreWarningDetails];
  }
  async persistChanges(changes, strict = false) {
    const durableChanges = changes.filter(isDurablePersistenceChange);
    if (durableChanges.length === 0) {
      return [];
    }
    if (!this.persistence) {
      if (strict) {
        throw new SessionArtifactValidationError(
          "artifact persistence is unavailable",
          "retention"
        );
      }
      const warnings = this.downgradeDurableChanges(durableChanges);
      this.applyDurableMarkers(durableChanges);
      return warnings;
    }
    const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
    for (const change of durableChanges) {
      if (change.action === "removed") continue;
      const stored = this.artifacts.get(change.artifactId);
      if (!stored) continue;
      const artifact = {
        ...toPublicArtifact(stored),
        persistedAt: recordedAt
      };
      change.artifact = artifact;
    }
    const sequence = this.persistenceSeq + 1;
    const payload = {
      v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
      sessionId: this.sessionId,
      sequence,
      recordedAt,
      changes: durableChanges.map(
        (change) => toPersistedChange(change, recordedAt)
      )
    };
    try {
      await this.persistence.recordEvent(payload);
      this.persistenceSeq = sequence;
      for (const change of durableChanges) {
        if (change.action === "removed") continue;
        const stored = this.artifacts.get(change.artifactId);
        if (!stored) continue;
        stored.persistedAt = recordedAt;
        stored.durableTombstoneRequired = true;
        change.artifact = toPublicArtifact(stored);
      }
      this.applyDurableMarkers(durableChanges);
      await this.maybeRecordSnapshot(recordedAt);
      return [];
    } catch (error) {
      if (strict) {
        throw error;
      }
      const reason = error instanceof Error ? error.message : String(error);
      const artifactIds = durableChanges.map((change) => change.artifactId);
      writeStderrLine(
        `[artifacts] session=${this.sessionId} action=persist_failed sequence=${payload.sequence} artifactIds=${JSON.stringify(
          artifactIds
        )} reason=${JSON.stringify(reason)}`
      );
      const warnings = this.downgradeDurableChanges(durableChanges);
      this.applyDurableMarkers(durableChanges);
      return warnings;
    }
  }
  async maybeRecordSnapshot(recordedAt) {
    if (!this.persistence) return;
    this.durableEventsSinceSnapshot++;
    const snapshotThreshold = SNAPSHOT_AFTER_DURABLE_EVENTS * Math.min(
      MAX_SNAPSHOT_BACKOFF_MULTIPLIER,
      2 ** this.consecutiveSnapshotFailures
    );
    if (this.durableEventsSinceSnapshot < snapshotThreshold) {
      return;
    }
    const sequence = this.persistenceSeq + 1;
    try {
      await this.persistence.recordSnapshot(
        this.buildSnapshotPayload(recordedAt, sequence)
      );
      this.persistenceSeq = sequence;
      this.durableEventsSinceSnapshot = 0;
      this.consecutiveSnapshotFailures = 0;
    } catch (error) {
      this.consecutiveSnapshotFailures = Math.min(
        this.consecutiveSnapshotFailures + 1,
        Math.log2(MAX_SNAPSHOT_BACKOFF_MULTIPLIER)
      );
      writeStderrLine(
        `[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
          error instanceof Error ? error.message : String(error)
        )}`
      );
    }
  }
  buildSnapshotPayload(recordedAt, sequence) {
    const artifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.retention !== "ephemeral").sort((a, b) => a.insertSeq - b.insertSeq).map((artifact) => toPersistedArtifact(artifact, recordedAt));
    const stickyEphemeralIds = Array.from(this.stickyEphemeralIds).filter(
      (id) => this.artifacts.has(id) || this.markerArtifacts.has(id)
    );
    const markerArtifacts = this.buildMarkerArtifacts(
      recordedAt,
      stickyEphemeralIds
    );
    return {
      v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
      sessionId: this.sessionId,
      sequence,
      recordedAt,
      artifacts,
      tombstonedIds: Array.from(this.tombstonedIds),
      stickyEphemeralIds,
      ...markerArtifacts.length > 0 ? { markerArtifacts } : {}
    };
  }
  buildMarkerArtifacts(recordedAt, stickyEphemeralIds) {
    const markerIds = [...this.tombstonedIds, ...stickyEphemeralIds];
    const artifacts = [];
    const seen = /* @__PURE__ */ new Set();
    for (const id of markerIds) {
      if (seen.has(id)) continue;
      seen.add(id);
      const live = this.artifacts.get(id);
      if (live) {
        artifacts.push(toPersistedArtifact(toPublicArtifact(live), recordedAt));
        continue;
      }
      const markerArtifact = this.markerArtifacts.get(id);
      if (markerArtifact) {
        artifacts.push(markerArtifact);
      }
    }
    return artifacts;
  }
  downgradeDurableChanges(changes) {
    let downgraded = false;
    let removalNotPersisted = false;
    for (const change of changes) {
      if (change.action === "removed") {
        removalNotPersisted = true;
        if (change.reason === "explicit") {
          this.rememberTombstone(change);
          this.stickyEphemeralIds.delete(change.artifactId);
        }
        continue;
      }
      const stored = this.artifacts.get(change.artifactId);
      if (!stored) continue;
      stored.retention = "ephemeral";
      stored.persistenceWarning = "persistence_unavailable";
      stored.durableTombstoneRequired = stored.durableTombstoneRequired || stored.persistedAt !== void 0;
      delete stored.persistedAt;
      change.artifact = toPublicArtifact(stored);
      downgraded = true;
    }
    const warnings = [];
    if (downgraded) {
      warnings.push(
        "artifact persistence unavailable; durable artifacts kept ephemeral"
      );
    }
    if (removalNotPersisted) {
      warnings.push("artifact removal not persisted; live removal kept");
    }
    return warnings;
  }
  applyDurableMarkers(changes) {
    for (const change of changes) {
      if (change.action === "removed") {
        if (change.reason === "explicit") {
          this.rememberTombstone(change);
          this.stickyEphemeralIds.delete(change.artifactId);
        } else if (change.reason === "eviction") {
          this.stickyEphemeralIds.delete(change.artifactId);
          this.markerArtifacts.delete(change.artifactId);
        } else if (change.reason === "unpin_to_ephemeral") {
          this.rememberStickyEphemeral(change);
        }
        continue;
      }
      if (change.artifact && change.artifact.retention !== "ephemeral") {
        this.tombstonedIds.delete(change.artifactId);
        this.tombstonedClientIds.delete(change.artifactId);
        this.stickyEphemeralIds.delete(change.artifactId);
        this.markerArtifacts.delete(change.artifactId);
      }
    }
  }
  rememberStickyEphemeral(change) {
    const artifactId = change.artifactId;
    this.stickyEphemeralIds.delete(artifactId);
    this.stickyEphemeralIds.add(artifactId);
    if (change.artifact) {
      this.markerArtifacts.set(
        artifactId,
        toPersistedArtifact(change.artifact, change.artifact.updatedAt)
      );
    }
    while (this.stickyEphemeralIds.size > MAX_STICKY_EPHEMERAL_IDS) {
      const oldest2 = this.stickyEphemeralIds.values().next().value;
      if (oldest2 === void 0) break;
      this.stickyEphemeralIds.delete(oldest2);
      this.markerArtifacts.delete(oldest2);
    }
  }
  rememberTombstone(change) {
    this.tombstonedIds.delete(change.artifactId);
    this.tombstonedIds.add(change.artifactId);
    this.tombstonedClientIds.set(
      change.artifactId,
      change.removedClientId ?? this.artifacts.get(change.artifactId)?.clientId
    );
    if (change.artifact) {
      this.markerArtifacts.set(
        change.artifactId,
        toPersistedArtifact(change.artifact, change.artifact.updatedAt)
      );
    }
    while (this.tombstonedIds.size > MAX_TOMBSTONED_IDS) {
      const oldest2 = this.tombstonedIds.values().next().value;
      if (oldest2 === void 0) break;
      writeStderrLine(
        `[artifacts] session=${this.sessionId} action=tombstone_evicted artifactId=${JSON.stringify(
          oldest2
        )} limit=${MAX_TOMBSTONED_IDS}`
      );
      this.tombstonedIds.delete(oldest2);
      this.tombstonedClientIds.delete(oldest2);
      this.markerArtifacts.delete(oldest2);
    }
  }
  setLastRestoreWarnings(warnings) {
    this.lastRestoreWarnings = [...warnings];
    this.lastRestoreWarningDetails = detailsForRestoreWarnings(warnings);
  }
  enqueue(operation) {
    const result = this.operationQueue.then(operation, operation);
    this.operationQueue = result.then(
      () => void 0,
      () => void 0
    );
    return result;
  }
  denyCrossClientMutation(action, artifactId, existing, options) {
    if (existing.source !== "client" || existing.clientId === void 0 || existing.clientId === options?.clientId) {
      return;
    }
    writeStderrLine(
      `[artifacts] session=${this.sessionId} action=${action}_denied artifactId=${artifactId} owner=${existing.clientId} requester=${options?.clientId ?? "<anonymous>"}`
    );
    throw new SessionArtifactAuthorizationError(
      this.sessionId,
      artifactId,
      existing.clientId,
      options?.clientId
    );
  }
  async normalizeInput(input, receivedSeq, trustedPublisherFromCaller, options = {}) {
    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,
      await this.getRealWorkspaceCwdForValidation()
    ) : 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 retention = normalizeRetention(input.retention, {
      persistenceAvailable: this.persistence !== void 0
    });
    const workspaceStatus = workspacePath ? await this.getInitialWorkspaceStatus(
      workspacePath,
      options.workspaceExpected,
      { hashContent: options.hashWorkspaceContent !== false }
    ) : void 0;
    if (workspaceStatus?.escaped) {
      throw new SessionArtifactValidationError(
        "workspacePath must stay inside the workspace",
        "workspacePath"
      );
    }
    const metadata = withWorkspaceContentHashMetadata(
      normalizeMetadata(input.metadata, {
        budget: options.metadataBudget ?? "user"
      }),
      workspaceStatus
    );
    const kind = normalizeKind(
      input.kind ?? inferKind({ storage, workspacePath, url })
    );
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const identityKey = buildIdentityKey({
      storage,
      workspacePath,
      managedId,
      url
    });
    const id = stableSessionArtifactId(this.sessionId, identityKey);
    return {
      id,
      identityKey,
      receivedSeq,
      retentionExplicit: input.retention !== void 0,
      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: workspaceStatus?.sizeBytes !== void 0 ? workspaceStatus.sizeBytes : input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) : void 0,
      metadata,
      retention,
      restoreState: "live",
      ...this.persistence === void 0 && retention !== "ephemeral" ? { persistenceWarning: "persistence_unavailable" } : {},
      clientRetained: source === "client" && (input.clientRetained !== void 0 ? input.clientRetained === true : true),
      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)
    };
  }
  shouldSuppressTombstonedUpsert(artifact) {
    if (!this.tombstonedIds.has(artifact.id)) {
      return false;
    }
    const tombstonedClientId = this.tombstonedClientIds.get(artifact.id);
    if (artifact.source !== "client") {
      return false;
    }
    return !(artifact.retentionExplicit && artifact.clientId !== void 0 && artifact.clientId === tombstonedClientId);
  }
  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
      })
    );
  }
  applyStickyEphemeralOverride(artifact) {
    if (!this.stickyEphemeralIds.has(artifact.id) || artifact.retentionExplicit || artifact.retention === "ephemeral") {
      return artifact;
    }
    return {
      ...artifact,
      retention: "ephemeral",
      retentionExplicit: true,
      persistenceWarning: "sticky_override_active"
    };
  }
  async getInitialWorkspaceStatus(workspacePath, expected, options = { hashContent: true }) {
    try {
      return await getWorkspaceStatus(
        workspacePath,
        this.getRealWorkspaceCwd(),
        expected,
        options
      );
    } 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(
        artifact.workspacePath,
        this.getRealWorkspaceCwd(),
        {
          sizeBytes: artifact.sizeBytes,
          mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
          sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
        }
      );
      const changed = isWorkspaceContentChanged(artifact, status);
      artifact.status = changed ? "changed" : status.status;
      if (!changed) {
        artifact.sizeBytes = status.sizeBytes;
      }
      if (status.escaped) {
        artifact.status = "missing";
        artifact.sizeBytes = void 0;
        artifact.hideWorkspacePath = true;
      }
      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 getRealWorkspaceCwdForValidation() {
    try {
      return await this.getRealWorkspaceCwd();
    } catch (error) {
      const reason = error instanceof Error ? error.message : String(error);
      throw new SessionArtifactValidationError(
        `workspacePath could not be inspected: ${reason}`,
        "workspacePath"
      );
    }
  }
  async evictOverflow(createdIds, changes, strict = false) {
    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),
        durableTombstoneRequired: artifact.durableTombstoneRequired,
        reason: "eviction"
      });
    }
    const overflowCreated = Array.from(this.artifacts.values()).filter((artifact) => createdInThisBatch.has(artifact.id)).sort((a, b) => b.receivedSeq - a.receivedSeq);
    if (strict && overflowCreated.length > 0 && this.artifacts.size > this.maxArtifacts) {
      throw new SessionArtifactValidationError(
        "artifact store is full; no eviction candidate is available",
        "artifactId"
      );
    }
    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,
      retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
      retentionSource: existing.retentionSource,
      retention: mergeRetention(existing, next),
      restoreState: "live",
      persistenceWarning: existing.persistenceWarning ?? next.persistenceWarning,
      persistedAt: existing.persistedAt ?? next.persistedAt,
      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,
    retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
    retention: mergeRetention(existing, next),
    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,
    hideWorkspacePath: publishedUpdate || existing.storage === "published" || incoming.workspacePath ? void 0 : existing.hideWorkspacePath,
    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),
    retention: mergeRetention(existing, incoming),
    restoreState: "live",
    persistenceWarning: incoming.retentionExplicit && incoming.retention !== "ephemeral" ? incoming.persistenceWarning : existing.persistenceWarning ?? incoming.persistenceWarning,
    persistedAt: incoming.retentionExplicit && incoming.retention === "ephemeral" ? void 0 : existing.persistedAt ?? incoming.persistedAt,
    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;
    delete next.hideWorkspacePath;
  }
  const changed = !publicArtifactsEqual(
    toPublicArtifact(existing),
    toPublicArtifact(next)
  );
  if (changed) {
    next.updatedAt = now;
  }
  return { artifact: changed ? next : existing, changed };
}
__name(mergeArtifact, "mergeArtifact");
function shouldRecordEphemeralUnpin(existing, incoming) {
  return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
}
__name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
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.retention === b.retention && a.restoreState === b.restoreState && a.persistenceWarning === b.persistenceWarning && a.persistedAt === b.persistedAt && a.clientRetained === b.clientRetained && a.createdAt === b.createdAt && a.updatedAt === b.updatedAt && 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 strongestRetention(a, b) {
  const rank = {
    ephemeral: 0,
    restorable: 1
  };
  return rank[b] > rank[a] ? b : a;
}
__name(strongestRetention, "strongestRetention");
function mergeRetention(existing, incoming) {
  if (incoming.retentionExplicit && incoming.retention === "ephemeral") {
    return "ephemeral";
  }
  if (existing.retentionExplicit && existing.retention === "ephemeral" && !incoming.retentionExplicit) {
    return "ephemeral";
  }
  return strongestRetention(existing.retention, incoming.retention);
}
__name(mergeRetention, "mergeRetention");
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 ((key === WORKSPACE_CONTENT_SHA256_METADATA_KEY || key === WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY) && merged[key] !== value) {
      merged[key] = value;
      changed = true;
    } else 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 metadataBudgetBytes(metadata, "persisted") <= 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 hideWorkspacePath = "hideWorkspacePath" in artifact && artifact.hideWorkspacePath === true;
  const {
    id,
    kind,
    storage,
    source,
    status,
    title,
    description,
    workspacePath,
    managedId,
    url,
    mimeType,
    sizeBytes,
    metadata,
    retention,
    restoreState,
    persistenceWarning,
    persistedAt,
    clientRetained,
    createdAt,
    updatedAt,
    toolCallId,
    toolName,
    hookEventName
  } = artifact;
  return {
    id,
    kind,
    storage,
    source,
    status,
    title,
    ...description ? { description } : {},
    ...workspacePath && !hideWorkspacePath ? { workspacePath } : {},
    ...managedId ? { managedId } : {},
    ...url ? { url } : {},
    ...mimeType ? { mimeType } : {},
    ...sizeBytes !== void 0 ? { sizeBytes } : {},
    ...metadata ? { metadata } : {},
    retention,
    ...restoreState ? { restoreState } : {},
    ...persistenceWarning ? { persistenceWarning } : {},
    ...persistedAt ? { persistedAt } : {},
    clientRetained,
    createdAt,
    updatedAt,
    ...toolCallId ? { toolCallId } : {},
    ...toolName ? { toolName } : {},
    ...hookEventName ? { hookEventName } : {}
  };
}
__name(toPublicArtifact, "toPublicArtifact");
function persistedArtifactToInput(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,
    source: artifact.source,
    retention: artifact.retention,
    clientRetained: artifact.clientRetained,
    toolCallId: artifact.toolCallId,
    toolName: artifact.toolName,
    hookEventName: artifact.hookEventName
  };
}
__name(persistedArtifactToInput, "persistedArtifactToInput");
function workspaceExpectedFromArtifact(artifact) {
  if (!artifact.workspacePath) {
    return void 0;
  }
  return {
    sizeBytes: artifact.sizeBytes,
    mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
    sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
  };
}
__name(workspaceExpectedFromArtifact, "workspaceExpectedFromArtifact");
function toPersistedChange(change, recordedAt) {
  return {
    action: change.action,
    artifactId: change.artifactId,
    ...change.artifact ? { artifact: toPersistedArtifact(change.artifact, recordedAt) } : {},
    ...change.reason ? { reason: change.reason } : {}
  };
}
__name(toPersistedChange, "toPersistedChange");
function toPersistedArtifact(artifact, recordedAt) {
  return {
    id: artifact.id,
    kind: artifact.kind,
    storage: artifact.storage,
    source: artifact.source,
    status: artifact.status === "changed" ? "available" : artifact.status,
    title: artifact.title,
    ...artifact.description ? { description: artifact.description } : {},
    ...artifact.workspacePath ? { workspacePath: artifact.workspacePath } : {},
    ...artifact.managedId ? { managedId: artifact.managedId } : {},
    ...artifact.url ? { url: artifact.url } : {},
    ...artifact.mimeType ? { mimeType: artifact.mimeType } : {},
    ...artifact.sizeBytes !== void 0 ? { sizeBytes: artifact.sizeBytes } : {},
    ...artifact.metadata ? { metadata: artifact.metadata } : {},
    retention: artifact.retention,
    clientRetained: artifact.clientRetained,
    createdAt: artifact.createdAt,
    updatedAt: artifact.updatedAt,
    persistedAt: artifact.persistedAt ?? recordedAt,
    ...artifact.toolCallId ? { toolCallId: artifact.toolCallId } : {},
    ...artifact.toolName ? { toolName: artifact.toolName } : {},
    ...artifact.hookEventName ? { hookEventName: artifact.hookEventName } : {}
  };
}
__name(toPersistedArtifact, "toPersistedArtifact");
function isFileArtifactUrl(raw) {
  if (typeof raw !== "string") return false;
  try {
    return new URL(raw).protocol === "file:";
  } catch {
    return false;
  }
}
__name(isFileArtifactUrl, "isFileArtifactUrl");
function isArtifactSnapshotCompletenessWarning(warning) {
  if (warning.startsWith("skipped stale event sequence ")) return false;
  return warning.startsWith("skipped ") || warning.includes(" list truncated to ");
}
__name(isArtifactSnapshotCompletenessWarning, "isArtifactSnapshotCompletenessWarning");
function shouldCommitBeforeDurablePersistence(change) {
  return change.action === "removed" && change.durableTombstoneRequired === true;
}
__name(shouldCommitBeforeDurablePersistence, "shouldCommitBeforeDurablePersistence");
function isDurablePersistenceChange(change) {
  if (change.action === "removed" && change.durableTombstoneRequired === true) {
    return true;
  }
  if (!change.artifact) return false;
  return change.artifact.retention !== "ephemeral" || change.artifact.persistenceWarning === "persistence_unavailable";
}
__name(isDurablePersistenceChange, "isDurablePersistenceChange");
function stripDurableTombstoneMarkers(changes) {
  for (const change of changes) {
    delete change.durableTombstoneRequired;
    delete change.removedClientId;
  }
}
__name(stripDurableTombstoneMarkers, "stripDurableTombstoneMarkers");
function detailsForRestoreWarnings(warnings) {
  return warnings.map((warning) => {
    if (warning.startsWith(RESTORE_FAILED_WARNING_PREFIX)) {
      return {
        code: "ARTIFACT_RESTORE_FAILED",
        operation: "restore",
        durability: "unavailable",
        retryable: true,
        message: warning
      };
    }
    if (warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX)) {
      return {
        code: "ARTIFACT_RESTORE_PARTIAL_FAILED",
        operation: "restore",
        durability: "live_only",
        retryable: true,
        message: warning
      };
    }
    return {
      code: "ARTIFACT_WARNING",
      operation: "restore",
      message: warning
    };
  });
}
__name(detailsForRestoreWarnings, "detailsForRestoreWarnings");
function detailsForPersistenceWarnings(warnings, changes, operation) {
  if (warnings.length === 0) return [];
  const artifactIds = changes.filter(isDurablePersistenceChange).map((change) => change.artifactId);
  return warnings.map((warning) => {
    if (warning === "artifact persistence unavailable; durable artifacts kept ephemeral") {
      return {
        code: "ARTIFACT_PERSISTENCE_UNAVAILABLE",
        operation,
        artifactIds,
        durability: "live_only",
        retryable: false,
        message: warning
      };
    }
    if (warning === "artifact removal not persisted; live removal kept") {
      return {
        code: "ARTIFACT_REMOVAL_NOT_PERSISTED",
        operation,
        artifactIds,
        durability: "live_only",
        retryable: true,
        message: warning
      };
    }
    return {
      code: "ARTIFACT_WARNING",
      operation,
      artifactIds,
      message: warning
    };
  });
}
__name(detailsForPersistenceWarnings, "detailsForPersistenceWarnings");
function persistenceFailureDetail(options) {
  const unavailable = options.error instanceof SessionArtifactValidationError && options.error.message === "artifact persistence is unavailable";
  return {
    code: unavailable ? "ARTIFACT_PERSISTENCE_UNAVAILABLE" : "ARTIFACT_PERSISTENCE_WRITE_FAILED",
    operation: options.operation,
    artifactIds: options.artifactIds,
    durability: "unavailable",
    retryable: !unavailable,
    message: options.message
  };
}
__name(persistenceFailureDetail, "persistenceFailureDetail");
function cloneStoredArtifact(artifact) {
  return {
    ...artifact,
    ...artifact.metadata ? { metadata: { ...artifact.metadata } } : {}
  };
}
__name(cloneStoredArtifact, "cloneStoredArtifact");
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 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 (hasSecretLikeUrlComponent(parsed)) {
    throw new SessionArtifactValidationError(
      "url must not include secret-like components",
      "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 hasSecretLikeUrlComponent(parsed) {
  for (const [key, value] of parsed.searchParams) {
    if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
      return true;
    }
  }
  for (const segment of parsed.pathname.split("/").filter(Boolean)) {
    let decodedSegment = segment;
    try {
      decodedSegment = decodeURIComponent(segment);
    } catch {
    }
    if (isSecretLikeUrlValue(decodedSegment)) {
      return true;
    }
  }
  const fragment = parsed.hash.slice(1);
  return hasSecretLikeUrlFragment(fragment);
}
__name(hasSecretLikeUrlComponent, "hasSecretLikeUrlComponent");
function hasSecretLikeUrlFragment(fragment) {
  if (!fragment) return false;
  const candidates = /* @__PURE__ */ new Set([fragment]);
  try {
    candidates.add(decodeURIComponent(fragment));
  } catch {
  }
  for (const candidate of candidates) {
    if (isSecretLikeUrlText(candidate) || isSecretLikeUrlValue(candidate)) {
      return true;
    }
    for (const [key, value] of new URLSearchParams(candidate)) {
      if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
        return true;
      }
    }
  }
  return false;
}
__name(hasSecretLikeUrlFragment, "hasSecretLikeUrlFragment");
function isSecretLikeUrlText(value) {
  const normalized = value.replace(/([a-z])([A-Z])/g, "$1-$2");
  return /(?:^|[-_.])(token|secret|password|passwd|pwd|cookie|authorization|credential|signature|sig|api[-_]?key|access[-_]?key)(?:$|[-_.=&#])/i.test(
    normalized
  );
}
__name(isSecretLikeUrlText, "isSecretLikeUrlText");
function isSecretLikeUrlValue(value) {
  return SECRET_TOKEN_VALUE_PATTERN.test(value.trim());
}
__name(isSecretLikeUrlValue, "isSecretLikeUrlValue");
function isSecretLikeMetadataValue(value) {
  return /^(?:bearer\s+\S{8,}|sk-[A-Za-z0-9_-]{12,}|(?:gh[pousr]|github_pat)_[A-Za-z0-9_/-]{12,})$/i.test(
    value.trim()
  );
}
__name(isSecretLikeMetadataValue, "isSecretLikeMetadataValue");
function normalizeMetadata(metadata, options = {}) {
  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 (isPrototypeMetadataKey(key)) {
      continue;
    }
    if (options.budget !== "persisted" && isReservedWorkspaceMetadataKey(key)) {
      continue;
    }
    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 (isSecretLikeUrlText(key)) {
      throw new SessionArtifactValidationError(
        "metadata keys must not contain secret-like names",
        "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"
      );
    }
    if (typeof value === "string" && !isReservedWorkspaceMetadataKey(key) && isSecretLikeMetadataValue(value)) {
      throw new SessionArtifactValidationError(
        "metadata string values must not contain secret-like tokens",
        "metadata"
      );
    }
    normalized[key] = value;
  }
  if (Object.keys(normalized).length === 0) {
    return void 0;
  }
  if (metadataBudgetBytes(normalized, options.budget ?? "user") > 4096) {
    throw new SessionArtifactValidationError(
      "metadata must be 4096 bytes or fewer",
      "metadata"
    );
  }
  return normalized;
}
__name(normalizeMetadata, "normalizeMetadata");
function normalizeRetention(value, options) {
  if (value === void 0) {
    return options.persistenceAvailable ? "restorable" : "ephemeral";
  }
  if (value === "ephemeral" || value === "restorable") {
    return value;
  }
  if (value === "pinned") {
    throw new SessionArtifactValidationError(
      "pinned retention is not supported by session_artifacts_persistence",
      "retention"
    );
  }
  throw new SessionArtifactValidationError(
    "retention must be ephemeral or restorable",
    "retention"
  );
}
__name(normalizeRetention, "normalizeRetention");
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(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
  const realWorkspace = await realWorkspaceCwd;
  const absolutePath = path2.resolve(realWorkspace, workspacePath);
  try {
    const realPath = await fs2.realpath(absolutePath);
    const relative = path2.relative(realWorkspace, realPath);
    if (!relative || isOutsidePath(relative)) {
      return { status: "missing", escaped: true };
    }
    const preOpenStat = await fs2.lstat(realPath);
    const handle = await fs2.open(
      realPath,
      fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
    );
    try {
      const stat = await handle.stat();
      if (!isSameFile(preOpenStat, stat)) {
        return { status: "missing", escaped: true };
      }
      if (stat.isFile()) {
        const expectedMtimeMs = typeof expected?.mtimeMs === "number" ? expected.mtimeMs : void 0;
        const expectedSha256 = typeof expected?.sha256 === "string" ? expected.sha256 : void 0;
        const unchanged = expected?.sizeBytes === stat.size && expectedMtimeMs === stat.mtimeMs;
        const sizeChanged = expected?.sizeBytes !== void 0 && expected.sizeBytes !== stat.size;
        if (sizeChanged) {
          return {
            status: "changed",
            sizeBytes: stat.size,
            mtimeMs: stat.mtimeMs
          };
        }
        if (unchanged) {
          return {
            status: "available",
            sizeBytes: stat.size,
            mtimeMs: stat.mtimeMs
          };
        }
        if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
          return {
            status: expectedSha256 ? "changed" : "available",
            sizeBytes: stat.size,
            mtimeMs: stat.mtimeMs
          };
        }
        if (!options.hashContent) {
          return {
            status: expectedSha256 ? "changed" : "available",
            sizeBytes: stat.size,
            mtimeMs: stat.mtimeMs
          };
        }
        const sha256 = await hashFile(handle);
        if (expectedSha256 && sha256 !== expectedSha256) {
          return {
            status: "changed",
            sizeBytes: stat.size,
            mtimeMs: stat.mtimeMs
          };
        }
        return {
          status: "available",
          sizeBytes: stat.size,
          mtimeMs: stat.mtimeMs,
          sha256
        };
      }
      return {
        status: "available"
      };
    } finally {
      await handle.close();
    }
  } catch (error) {
    if (isNoFollowSymlinkError(error)) {
      return { status: "missing", escaped: true };
    }
    if (!isNotFoundError(error)) {
      throw error;
    }
    if (await danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace)) {
      return { status: "missing", escaped: true };
    }
    return { status: "missing" };
  }
}
__name(getWorkspaceStatus, "getWorkspaceStatus");
function isSameFile(before, after) {
  return before.dev === after.dev && before.ino === after.ino;
}
__name(isSameFile, "isSameFile");
async function hashFile(handle) {
  const hash = createHash("sha256");
  for await (const chunk of handle.createReadStream({ start: 0 })) {
    hash.update(chunk);
  }
  return hash.digest("hex");
}
__name(hashFile, "hashFile");
function withWorkspaceContentHashMetadata(metadata, workspaceStatus) {
  if (workspaceStatus?.status !== "available" || !workspaceStatus.sha256) {
    return metadata;
  }
  const next = {
    ...metadata ?? {},
    [WORKSPACE_CONTENT_SHA256_METADATA_KEY]: workspaceStatus.sha256,
    ...workspaceStatus.mtimeMs !== void 0 ? { [WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY]: workspaceStatus.mtimeMs } : {}
  };
  return next;
}
__name(withWorkspaceContentHashMetadata, "withWorkspaceContentHashMetadata");
function isWorkspaceContentChanged(artifact, status) {
  if (status.status !== "available") {
    return false;
  }
  const expectedSha256 = artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY];
  if (artifact.sizeBytes !== void 0 && status.sizeBytes !== void 0 && status.sizeBytes !== artifact.sizeBytes) {
    return true;
  }
  return typeof expectedSha256 === "string" && status.sha256 !== void 0 && status.sha256 !== expectedSha256;
}
__name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
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 isNoFollowSymlinkError(error) {
  if (typeof error !== "object" || error === null || !("code" in error)) {
    return false;
  }
  return error.code === "ELOOP";
}
__name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
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 };
  }
};
var KNOWN_SESSION_UPDATE_TYPES = /* @__PURE__ */ new Set([
  "user_message_chunk",
  "agent_message_chunk",
  "agent_thought_chunk",
  "tool_call",
  "tool_call_update",
  "plan",
  "available_commands_update",
  "current_mode_update",
  "config_option_update",
  "session_info_update",
  "usage_update"
]);
var MAX_BULK_REPLAY_UPDATES = 1e4;
function isRecord2(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
__name(isRecord2, "isRecord");
function isBulkReplayUpdate(value) {
  if (!isRecord2(value)) return false;
  const updateType = value["sessionUpdate"];
  return typeof updateType === "string" && KNOWN_SESSION_UPDATE_TYPES.has(updateType);
}
__name(isBulkReplayUpdate, "isBulkReplayUpdate");
function describeLoadReplayValue(value) {
  if (Array.isArray(value)) return "array";
  if (value === null) return "null";
  return typeof value;
}
__name(describeLoadReplayValue, "describeLoadReplayValue");
function extractLoadReplayResponse(state) {
  const meta = isRecord2(state._meta) ? state._meta : void 0;
  const replay = meta?.[LOAD_REPLAY_META_KEY];
  if (replay === void 0) return { state, updates: [] };
  if (!isRecord2(replay) || replay["v"] !== LOAD_REPLAY_VERSION) {
    const version = isRecord2(replay) ? replay["v"] : void 0;
    throw new Error(
      `Invalid qwen.session.loadReplay payload (type=${describeLoadReplayValue(replay)}, version=${JSON.stringify(version)})`
    );
  }
  const rawUpdates = replay["updates"];
  if (!Array.isArray(rawUpdates)) {
    throw new Error(
      `Invalid qwen.session.loadReplay updates (version=${LOAD_REPLAY_VERSION}, count=not-array)`
    );
  }
  if (rawUpdates.length > MAX_BULK_REPLAY_UPDATES) {
    throw new Error(
      `qwen.session.loadReplay updates exceed limit (${rawUpdates.length} > ${MAX_BULK_REPLAY_UPDATES})`
    );
  }
  const partial = replay["partial"];
  if (partial !== void 0 && partial !== true) {
    throw new Error(
      `Invalid qwen.session.loadReplay partial (version=${LOAD_REPLAY_VERSION}, partial=${JSON.stringify(partial)})`
    );
  }
  const replayError = replay["replayError"];
  if (replayError !== void 0 && typeof replayError !== "string") {
    throw new Error(
      `Invalid qwen.session.loadReplay replayError (version=${LOAD_REPLAY_VERSION}, replayError=${describeLoadReplayValue(replayError)})`
    );
  }
  const invalidUpdateIndex = rawUpdates.findIndex(
    (update) => !isBulkReplayUpdate(update)
  );
  if (invalidUpdateIndex !== -1) {
    const invalidUpdate = rawUpdates[invalidUpdateIndex];
    const discriminator = isRecord2(invalidUpdate) ? invalidUpdate["sessionUpdate"] : void 0;
    throw new Error(
      `Invalid qwen.session.loadReplay update at index ${invalidUpdateIndex} (version=${LOAD_REPLAY_VERSION}, count=${rawUpdates.length}, sessionUpdate=${JSON.stringify(discriminator)})`
    );
  }
  const nextMeta = { ...meta ?? {} };
  delete nextMeta[LOAD_REPLAY_META_KEY];
  const cleanState = { ...state };
  if (Object.keys(nextMeta).length > 0) {
    cleanState._meta = nextMeta;
  } else {
    delete cleanState._meta;
  }
  return {
    state: cleanState,
    updates: rawUpdates,
    ...partial === true ? { partial: true } : {},
    ...typeof replayError === "string" ? { replayError } : {}
  };
}
__name(extractLoadReplayResponse, "extractLoadReplayResponse");
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 touchedScopesFromTopics(topics) {
  const scopes = /* @__PURE__ */ new Set();
  for (const topic of topics) {
    scopes.add(topic === "user" || topic === "feedback" ? "user" : "project");
  }
  return ["user", "project"].filter((scope) => scopes.has(scope));
}
__name(touchedScopesFromTopics, "touchedScopesFromTopics");
function isBridgeMemoryScope(value) {
  return value === "user" || value === "project";
}
__name(isBridgeMemoryScope, "isBridgeMemoryScope");
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 touchedScopes = record["touchedScopes"];
  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) || touchedScopes !== void 0 && (!Array.isArray(touchedScopes) || !touchedScopes.every(isBridgeMemoryScope))) {
    throw new Error("Malformed workspace memory forget response");
  }
  const parsedTouchedTopics = touchedTopics;
  return {
    ...summary === void 0 ? {} : { summary },
    removedEntries: parsedRemovedEntries,
    touchedTopics: parsedTouchedTopics,
    touchedScopes: touchedScopes === void 0 ? touchedScopesFromTopics(parsedTouchedTopics) : touchedScopes
  };
}
__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 pickUserInputEchoMeta(meta) {
  if (!meta || typeof meta !== "object" || Array.isArray(meta)) return {};
  const inputAnnotations = meta["inputAnnotations"];
  return Array.isArray(inputAnnotations) ? { inputAnnotations } : {};
}
__name(pickUserInputEchoMeta, "pickUserInputEchoMeta");
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: {
              ...pickUserInputEchoMeta(req._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 classifyTurnErrorKind(message) {
  return message.trim().toLowerCase() === "terminated" ? "model_stream_interrupted" : void 0;
}
__name(classifyTurnErrorKind, "classifyTurnErrorKind");
function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId) {
  const message = extractErrorMessage(err);
  const code = extractErrorCode(err);
  const errorKind = classifyTurnErrorKind(message);
  if (errorKind) {
    writeServeDebugLine(
      `turn_error classified session=${JSON.stringify(sessionId)} message=${JSON.stringify(message)} errorKind=${JSON.stringify(errorKind)}` + (code ? ` code=${JSON.stringify(code)}` : "") + (promptId ? ` promptId=${JSON.stringify(promptId)}` : "")
    );
  }
  entry.retryAllowed = true;
  entry.turnError = {
    message,
    ...code ? { code } : {},
    ...errorKind ? { errorKind } : {}
  };
  try {
    entry.events.publish({
      type: "turn_error",
      data: {
        sessionId,
        message,
        ...code ? { code } : {},
        ...errorKind ? { errorKind } : {},
        ...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 MAX_PARENT_PERSIST_ATTEMPTS = 3;
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_GENERATION_TIMEOUT_MS = 65e3;
var GENERATION_STREAM_QUEUE_CAPACITY = 128;
var SESSION_BTW_TIMEOUT_MS = 6e4;
var SESSION_TRANSCRIPT_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;
  }
  const reserveFreshSession = /* @__PURE__ */ __name((context) => opts.freshSessionAdmission?.(context), "reserveFreshSession");
  const releaseFreshSessionReservation = /* @__PURE__ */ __name((reservation) => {
    if (!reservation) return;
    try {
      reservation.release();
    } catch (err) {
      opts.onDiagnosticLine?.(
        `qwen serve: fresh session admission release failed: ${err instanceof Error ? err.message : String(err)}`,
        "warn"
      );
    }
  }, "releaseFreshSessionReservation");
  const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
    try {
      opts.sessionLifecycle?.(event);
    } catch (err) {
      const message = `qwen serve: session lifecycle callback failed: ${err instanceof Error ? err.message : String(err)}`;
      opts.onDiagnosticLine?.(message, "warn");
      writeStderrLine(message);
    }
  }, "emitSessionLifecycle");
  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 compactedReplayMaxBytes = normalizeCompactedReplayMaxBytes(
    opts.compactedReplayMaxBytes
  );
  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 workspaceMcpStatusCache;
  const workspaceMcpToolsCache = /* @__PURE__ */ new Map();
  const workspaceMcpResourcesCache = /* @__PURE__ */ new Map();
  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 && !ci.workspaceMcpDiscoveryInFlight && ci.workspaceMcpAuthenticationServerNames.size === 0 && inFlightSpawnCount === 0;
  }
  __name(hasNoChannelWork, "hasNoChannelWork");
  function beginWorkspaceMcpDiscovery(ci) {
    workspaceMcpStatusCache = void 0;
    workspaceMcpToolsCache.clear();
    workspaceMcpResourcesCache.clear();
    ci.workspaceMcpDiscoveryInFlight = true;
    if (ci.workspaceMcpDiscoveryTimer) {
      clearTimeout(ci.workspaceMcpDiscoveryTimer);
    }
    ci.workspaceMcpDiscoveryTimer = setTimeout(() => {
      ci.workspaceMcpDiscoveryTimer = void 0;
      ci.workspaceMcpDiscoveryInFlight = false;
      ci.workspaceMcpDiscoveryRequested = false;
      if (hasNoChannelWork(ci)) {
        void startIdleTimer(ci, "workspace MCP discovery timeout");
      }
    }, MCP_RESTART_TIMEOUT_MS);
    ci.workspaceMcpDiscoveryTimer.unref();
  }
  __name(beginWorkspaceMcpDiscovery, "beginWorkspaceMcpDiscovery");
  function finishWorkspaceMcpDiscovery(ci) {
    ci.workspaceMcpDiscoveryInFlight = false;
    if (ci.workspaceMcpDiscoveryTimer) {
      clearTimeout(ci.workspaceMcpDiscoveryTimer);
      ci.workspaceMcpDiscoveryTimer = void 0;
    }
  }
  __name(finishWorkspaceMcpDiscovery, "finishWorkspaceMcpDiscovery");
  function invalidateWorkspaceMcpDetailCache(serverName) {
    workspaceMcpToolsCache.delete(serverName);
    workspaceMcpResourcesCache.delete(serverName);
  }
  __name(invalidateWorkspaceMcpDetailCache, "invalidateWorkspaceMcpDetailCache");
  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 generationRequests = /* @__PURE__ */ new Map();
  const inFlightExtensionRefreshes = /* @__PURE__ */ new Map();
  const toSessionSummary = /* @__PURE__ */ __name((entry) => {
    let isWaitingForPermission = false;
    let isWaitingForUserQuestion = false;
    for (const interaction of entry.pendingInteractions.values()) {
      if (interaction.kind === "user_question") {
        isWaitingForUserQuestion = true;
      } else {
        isWaitingForPermission = true;
      }
    }
    return {
      sessionId: entry.sessionId,
      workspaceCwd: entry.workspaceCwd,
      createdAt: entry.createdAt,
      displayName: entry.displayName,
      ...entry.parentSessionId ? { parentSessionId: entry.parentSessionId } : {},
      ...entry.sourceType ? { sourceType: entry.sourceType } : {},
      ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
      clientCount: entry.clientIds.size,
      hasActivePrompt: entry.promptActive,
      isWaitingForPermission,
      isWaitingForUserQuestion,
      pendingInteractionCount: entry.pendingInteractions.size,
      hasTurnError: entry.turnError !== void 0,
      ...entry.turnError !== void 0 ? { turnError: entry.turnError } : {},
      pendingInteractions: [...entry.pendingInteractions.values()]
    };
  }, "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;
  let shutdownPromise;
  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 rollbackAttachRegistration = /* @__PURE__ */ __name(async (entry, clientId, attachCountDelta = 1) => {
    entry.attachCount = Math.max(0, entry.attachCount - attachCountDelta);
    unregisterClient(entry, clientId);
    if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
      await bridgeApi.killSession(entry.sessionId).catch(() => {
      });
    } else if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && !entry.promptActive) {
      await closeSessionImpl(entry.sessionId, void 0, {
        reason: "last_client_detached"
      }).catch((err) => {
        writeStderrLine(
          `qwen serve: close-on-attach-rollback failed for ${JSON.stringify(entry.sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
        );
      });
    }
  }, "rollbackAttachRegistration");
  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),
        // Daemon token-burn accounting: forward per-round token usage observed
        // at the session/update fan-in to the daemon host's metrics ring via
        // the telemetry seam. Optional-chained so non-daemon callers (tests,
        // Mode A) that wire no `tokenUsage` metric are a silent no-op.
        (inputTokens, outputTokens, durationMs, apiErrors, apiRetries) => telemetry.metrics?.tokenUsage?.(
          inputTokens,
          outputTokens,
          durationMs,
          apiErrors,
          apiRetries
        ),
        // `create_sub_session` tool: forward the request/response hook so a child
        // tool can ask the daemon to spawn a sub-session and (for 'first-turn')
        // return its result. Omitted → the method reports daemon-only.
        opts.onCreateSubSession,
        (sessionId, event) => {
          const request = generationRequests.get(event.requestId);
          if (!request || request.sessionId !== sessionId) return;
          if (request.queue.push(event)) return;
          request.settled = true;
          generationRequests.delete(event.requestId);
          request.queue.fail(new Error("Generation stream consumer too slow"));
          void request.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionGenerationCancel, {
            sessionId,
            requestId: event.requestId
          }).catch(() => void 0);
        }
      );
      const connection = new ClientSideConnection(() => client, channel.stream);
      const info = {
        channel,
        connection,
        client,
        sessionIds,
        pendingRestoreIds: /* @__PURE__ */ new Set(),
        sessionSpawnsInFlight: 0,
        workspaceControlInFlight: 0,
        workspaceMcpDiscoveryInFlight: false,
        workspaceMcpDiscoveryRequested: false,
        workspaceMcpAuthenticationServerNames: /* @__PURE__ */ new Set(),
        workspaceMcpAuthenticationTimers: /* @__PURE__ */ new Map(),
        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();
        if (info.workspaceMcpDiscoveryTimer) {
          clearTimeout(info.workspaceMcpDiscoveryTimer);
          info.workspaceMcpDiscoveryTimer = void 0;
        }
        for (const timer of info.workspaceMcpAuthenticationTimers.values()) {
          clearTimeout(timer);
        }
        info.workspaceMcpAuthenticationTimers.clear();
        info.workspaceMcpAuthenticationServerNames.clear();
        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");
          emitSessionLifecycle({
            type: "removed",
            sessionId: sid,
            workspaceCwd: sessEntry.workspaceCwd,
            reason: "channel_closed"
          });
          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, approvalMode, requestedClientId, onSessionRegistered, parentSessionId, sourceType, sourceId) {
    const ci = await ensureChannel();
    ci.sessionSpawnsInFlight++;
    let sessionRegistered = false;
    let sessionRemovedDuringInitialization = false;
    let initializedSessionId;
    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,
        void 0,
        { parentSessionId, sourceType, sourceId }
      );
      initializedSessionId = entry.sessionId;
      sessionRegistered = true;
      onSessionRegistered?.();
      seedSnapshotCaches(entry, newSessionResp);
      const clientId = registerClient(entry, requestedClientId);
      let parentSessionPersisted;
      if (entry.parentSessionId) {
        const parentDeadline = Date.now() + initTimeoutMs;
        let lastParentErr;
        for (let attempt = 1; attempt <= MAX_PARENT_PERSIST_ATTEMPTS; attempt++) {
          const remaining = parentDeadline - Date.now();
          if (remaining <= 0) {
            parentSessionPersisted = false;
            lastParentErr = "deadline exceeded";
            break;
          }
          try {
            const parentResult = await Promise.race([
              withTimeout(
                entry.connection.extMethod(
                  SERVE_CONTROL_EXT_METHODS.sessionParent,
                  {
                    sessionId: entry.sessionId,
                    parentSessionId: entry.parentSessionId
                  }
                ),
                remaining,
                "sessionParent"
              ),
              getTransportClosedReject(entry)
            ]);
            parentSessionPersisted = parentResult?.persisted === true;
            break;
          } catch (err) {
            lastParentErr = err instanceof Error ? err.message : String(err);
            const terminal = err instanceof BridgeTimeoutError || err instanceof BridgeChannelClosedError || attempt === MAX_PARENT_PERSIST_ATTEMPTS;
            if (terminal) {
              parentSessionPersisted = false;
              break;
            }
          }
        }
        if (parentSessionPersisted === false) {
          writeStderrLine(
            `qwen serve: parentSessionId for ${entry.sessionId} was not persisted (${lastParentErr ?? "unknown"}) \u2014 the parent link is live-only until restart (reported to the caller via parentSessionPersisted=false)`
          );
        }
      }
      let sourcePersisted;
      if (entry.sourceType) {
        try {
          const sourceResult = await Promise.race([
            withTimeout(
              entry.connection.extMethod(
                SERVE_CONTROL_EXT_METHODS.sessionSource,
                {
                  sessionId: entry.sessionId,
                  sourceType: entry.sourceType,
                  ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {}
                }
              ),
              initTimeoutMs,
              "sessionSource"
            ),
            getTransportClosedReject(entry)
          ]);
          sourcePersisted = sourceResult?.persisted === true;
        } catch (err) {
          sourcePersisted = false;
          writeStderrLine(
            `qwen serve: source metadata for ${entry.sessionId} was not persisted (${err instanceof Error ? err.message : String(err)}) \u2014 the source is live-only until restart (reported to the caller via sourcePersisted=false)`
          );
        }
      }
      if (modelServiceId) {
        await applyModelServiceId(
          entry,
          modelServiceId,
          initTimeoutMs,
          clientId
        ).catch(() => {
        });
      }
      if (approvalMode) {
        try {
          await applyApprovalMode(entry, approvalMode, false, clientId);
        } catch (err) {
          try {
            await closeSessionImpl(entry.sessionId, void 0, {
              reason: "approval_mode_initialization_failed"
            });
            sessionRemovedDuringInitialization = true;
          } catch {
          }
          throw err;
        }
      }
      if (!byId.has(entry.sessionId)) {
        throw new Error(
          `Session ${entry.sessionId} died during session initialization`
        );
      }
      if (effectiveScope === "single" && !defaultEntry) defaultEntry = entry;
      return {
        sessionId: entry.sessionId,
        workspaceCwd: entry.workspaceCwd,
        attached: false,
        clientId,
        createdAt: entry.createdAt,
        ...entry.sourceType ? { sourceType: entry.sourceType } : {},
        ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
        ...entry.sourceType ? { sourcePersisted: sourcePersisted === true } : {},
        ...entry.parentSessionId ? { parentSessionPersisted: parentSessionPersisted === true } : {}
      };
    } finally {
      ci.sessionSpawnsInFlight = Math.max(0, ci.sessionSpawnsInFlight - 1);
      if (!sessionRegistered) {
        await reapPendingEmptyChannel(ci);
      } else if (sessionRemovedDuringInitialization && hasNoChannelWork(ci)) {
        await reapPendingEmptyChannel(ci);
        if (!ci.isDying) {
          await startIdleTimer(
            ci,
            `approval-mode initialization failure "${initializedSessionId}"`
          );
        }
      } else if (sessionRegistered && hasNoChannelWork(ci) && !ci.isDying) {
        await startIdleTimer(
          ci,
          `session orphaned during initialization "${initializedSessionId}"`
        );
      }
    }
  }
  __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");
  async function applyApprovalMode(entry, mode, persist, originatorClientId) {
    if (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: entry.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 (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;
    }
  }
  __name(applyApprovalMode, "applyApprovalMode");
  async function applyApprovalModeForAttach(entry, mode, clientId) {
    try {
      await applyApprovalMode(entry, mode, false, clientId);
    } catch (err) {
      await rollbackAttachRegistration(entry, clientId);
      throw err;
    }
  }
  __name(applyApprovalModeForAttach, "applyApprovalModeForAttach");
  const cancelPendingForSession = /* @__PURE__ */ __name((sessionId) => {
    permissionMediator.forgetSession(sessionId);
    byId.get(sessionId)?.pendingPermissionIds.clear();
    byId.get(sessionId)?.pendingInteractions.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 cacheWorkspaceMcpDetails = /* @__PURE__ */ __name(async (info, status) => {
    if (!Array.isArray(status.servers)) return;
    const serverNames = status.servers.flatMap(
      (server) => isRecord2(server) && typeof server["name"] === "string" && server["mcpStatus"] === "connected" ? [server["name"]] : []
    );
    const cacheDetail = /* @__PURE__ */ __name(async (serverName, method, cache) => {
      try {
        const result = await withTimeout(
          Promise.race([
            info.connection.extMethod(method, {
              serverName,
              cwd: boundWorkspace
            }),
            getChannelClosedReject(info)
          ]),
          initTimeoutMs,
          method
        );
        cache.set(serverName, result);
      } catch {
      }
    }, "cacheDetail");
    await Promise.all(
      serverNames.flatMap((serverName) => [
        cacheDetail(
          serverName,
          SERVE_STATUS_EXT_METHODS.workspaceMcpTools,
          workspaceMcpToolsCache
        ),
        cacheDetail(
          serverName,
          SERVE_STATUS_EXT_METHODS.workspaceMcpResources,
          workspaceMcpResourcesCache
        )
      ])
    );
  }, "cacheWorkspaceMcpDetails");
  const mergeManagedWorkspaceMcpStatus = /* @__PURE__ */ __name((serverNames, previous, current) => {
    if (Array.isArray(current.servers) && previous?.discoveryState === "completed" && current.discoveryState === "not_started") {
      if (current.servers.length === 0) return previous;
      const currentServers = new Map(
        current.servers.map((server) => [server.name, server])
      );
      const previousNames = new Set(
        previous.servers.map((server) => server.name)
      );
      const servers = previous.servers.map(
        (server) => serverNames.has(server.name) ? currentServers.get(server.name) ?? server : server
      );
      for (const server of current.servers) {
        if (serverNames.has(server.name) && !previousNames.has(server.name)) {
          servers.push(server);
        }
      }
      return {
        ...previous,
        discoveryState: "completed",
        servers
      };
    }
    return current;
  }, "mergeManagedWorkspaceMcpStatus");
  const requestWorkspaceStatus = /* @__PURE__ */ __name(async (method, idle, params = {}, managedServerNames) => {
    const info = liveChannelInfo();
    if (!info) {
      if (method === SERVE_STATUS_EXT_METHODS.workspaceMcp && workspaceMcpStatusCache) {
        return workspaceMcpStatusCache;
      }
      return idle();
    }
    let response = await withTimeout(
      Promise.race([
        info.connection.extMethod(method, { ...params, cwd: boundWorkspace }),
        getChannelClosedReject(info)
      ]),
      initTimeoutMs,
      method
    );
    if (method === SERVE_STATUS_EXT_METHODS.workspaceMcp) {
      const rawStatus = response;
      if (!Array.isArray(rawStatus.servers)) {
        return response;
      }
      const rawServers = rawStatus.servers;
      const effectiveManagedServerNames = /* @__PURE__ */ new Set([
        ...info.workspaceMcpAuthenticationServerNames,
        ...managedServerNames ?? []
      ]);
      if (effectiveManagedServerNames.size > 0 || workspaceMcpStatusCache?.discoveryState === "completed" && rawStatus.discoveryState === "not_started" && rawServers.length === 0) {
        response = mergeManagedWorkspaceMcpStatus(
          effectiveManagedServerNames,
          workspaceMcpStatusCache,
          rawStatus
        );
      }
      const status = response;
      if (status.discoveryState === "completed") {
        await cacheWorkspaceMcpDetails(
          info,
          effectiveManagedServerNames.size > 0 ? {
            servers: rawServers.filter(
              (server) => effectiveManagedServerNames.has(server.name)
            )
          } : status
        );
      }
      if (info.workspaceMcpDiscoveryInFlight && (status.discoveryState === "completed" || Array.isArray(status.errors) && status.errors.length > 0)) {
        finishWorkspaceMcpDiscovery(info);
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace MCP discovery complete");
        }
      }
      if (status.discoveryState === "completed") {
        info.workspaceMcpDiscoveryRequested = true;
      } else if (status.discoveryState === "in_progress") {
        info.workspaceMcpDiscoveryRequested = true;
      } else if (Array.isArray(status.errors) && status.errors.length > 0) {
        info.workspaceMcpDiscoveryRequested = false;
      }
      let authenticationCompleted = false;
      for (const serverName of info.workspaceMcpAuthenticationServerNames) {
        const server = rawServers.find(
          (candidate) => candidate.name === serverName
        );
        if (server?.authenticationState !== "pending" && (server !== void 0 || rawStatus.discoveryState === "completed")) {
          info.workspaceMcpAuthenticationServerNames.delete(serverName);
          const timer = info.workspaceMcpAuthenticationTimers.get(serverName);
          if (timer) clearTimeout(timer);
          info.workspaceMcpAuthenticationTimers.delete(serverName);
          authenticationCompleted = true;
        }
      }
      if (authenticationCompleted) {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace MCP authentication complete");
        }
      }
      workspaceMcpStatusCache = response;
    }
    return response;
  }, "requestWorkspaceStatus");
  const STALE_CHILD_RESOURCE_MS = 3e4;
  let childResourceRefreshing = false;
  const refreshChildResource = /* @__PURE__ */ __name(async () => {
    if (childResourceRefreshing) return;
    const info = liveChannelInfo();
    if (!info) return;
    childResourceRefreshing = true;
    try {
      const res = await requestWorkspaceStatus(SERVE_STATUS_EXT_METHODS.workspaceResource, () => ({}));
      if (liveChannelInfo() !== info) return;
      if (typeof res.rssBytes === "number" && Number.isFinite(res.rssBytes)) {
        info.childRssBytes = res.rssBytes;
      }
      if (typeof res.cpuPercent === "number" && Number.isFinite(res.cpuPercent)) {
        info.childCpuPercent = Math.min(100, Math.max(0, res.cpuPercent));
      }
      info.childResourceAt = Date.now();
    } catch (err) {
      teeServeDebugLine(
        `child-resource refresh failed: ${err instanceof Error ? err.message : String(err)}`
      );
    } finally {
      childResourceRefreshing = false;
    }
  }, "refreshChildResource");
  const getChildResourceSnapshot = /* @__PURE__ */ __name(() => {
    const info = liveChannelInfo();
    if (!info || info.childResourceAt === void 0) return void 0;
    if (Date.now() - info.childResourceAt > STALE_CHILD_RESOURCE_MS) {
      return void 0;
    }
    return {
      rssBytes: info.childRssBytes ?? 0,
      cpuPercent: info.childCpuPercent ?? 0
    };
  }, "getChildResourceSnapshot");
  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({
      maxReplayBytes: compactedReplayMaxBytes,
      onReplayWindowEviction: /* @__PURE__ */ __name((eviction) => {
        teeServeDebugLine(
          `replay window evicted ${JSON.stringify(eviction)}`
        );
      }, "onReplayWindowEviction")
    })
  ), "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(), options = {}) => {
    const entry = {
      sessionId,
      workspaceCwd,
      createdAt: (/* @__PURE__ */ new Date()).toISOString(),
      ...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
      ...options.sourceType ? { sourceType: options.sourceType } : {},
      ...options.sourceId !== void 0 ? { sourceId: options.sourceId } : {},
      channel: ci.channel,
      connection: ci.connection,
      events,
      artifacts: new SessionArtifactStore({
        sessionId,
        workspaceCwd,
        persistence: createSessionArtifactPersistence(ci.connection, sessionId)
      }),
      recordingDegraded: false,
      promptQueue: Promise.resolve(),
      pendingPromptCount: 0,
      pendingPromptList: [],
      midTurnMessageQueue: [],
      modelChangeQueue: Promise.resolve(),
      approvalModeQueue: Promise.resolve(),
      modelPublishGeneration: 0,
      approvalModePublishGeneration: 0,
      pendingPermissionIds: /* @__PURE__ */ new Set(),
      pendingInteractions: /* @__PURE__ */ new Map(),
      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");
    emitSessionLifecycle({
      type: "registered",
      sessionId: entry.sessionId,
      workspaceCwd: entry.workspaceCwd,
      reason: options.lifecycleReason ?? "spawn"
    });
    if (options.drainEarlyEvents !== false) {
      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 artifactReseedChanges = /* @__PURE__ */ __name((before, after) => {
    const beforeById = new Map(
      before.map((artifact) => [artifact.id, artifact])
    );
    const afterById = new Map(after.map((artifact) => [artifact.id, artifact]));
    const changes = [];
    for (const artifact of before) {
      if (!afterById.has(artifact.id)) {
        changes.push({
          action: "removed",
          artifactId: artifact.id,
          artifact,
          reason: "eviction"
        });
      }
    }
    for (const artifact of after) {
      const previous = beforeById.get(artifact.id);
      if (!previous) {
        changes.push({
          action: "created",
          artifactId: artifact.id,
          artifact
        });
        continue;
      }
      if (!publicArtifactsEqual(previous, artifact)) {
        changes.push({
          action: "updated",
          artifactId: artifact.id,
          artifact
        });
      }
    }
    return changes;
  }, "artifactReseedChanges");
  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,
      retention: artifact.retention,
      clientRetained: artifact.clientRetained,
      source: "client"
    };
    if (clientId) {
      input.clientId = clientId;
    }
    return input;
  }, "makeClientArtifactInput");
  function createSessionArtifactPersistence(connection, sessionId) {
    return {
      recordEvent: /* @__PURE__ */ __name(async (payload) => {
        await connection.extMethod(
          SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
          {
            sessionId,
            kind: "event",
            payload
          }
        );
      }, "recordEvent"),
      recordSnapshot: /* @__PURE__ */ __name(async (payload) => {
        await connection.extMethod(
          SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
          {
            sessionId,
            kind: "snapshot",
            payload
          }
        );
      }, "recordSnapshot")
    };
  }
  __name(createSessionArtifactPersistence, "createSessionArtifactPersistence");
  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 replayStatus = action === "load" && entry.restoreReplayPartial === true ? {
      partial: true,
      ...typeof entry.restoreReplayError === "string" ? { replayError: entry.restoreReplayError } : {}
    } : {};
    const snapshot = entry.events.snapshotReplay();
    if (!snapshot) {
      return { lastEventId: entry.events.lastEventId, ...replayStatus };
    }
    if (action === "load") {
      return {
        compactedReplay: snapshot.compactedTurns,
        liveJournal: snapshot.liveJournal,
        lastEventId: snapshot.lastEventId,
        ...replayStatus
      };
    }
    return { lastEventId: snapshot.lastEventId, ...replayStatus };
  }, "replayFieldsFor");
  const restoredArtifactSnapshotFromState = /* @__PURE__ */ __name((state) => {
    const candidate = state.artifactSnapshot;
    const warnings = [];
    const snapshot = normalizeSnapshotPayload(candidate, warnings);
    if (!snapshot) return void 0;
    const snapshotWarnings = isRecord2(candidate) && Array.isArray(candidate["warnings"]) ? candidate["warnings"].filter(
      (warning) => typeof warning === "string" && warning.length <= 1e3
    ).slice(-500) : [];
    return {
      v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
      sessionId: snapshot.sessionId,
      sequence: snapshot.sequence,
      artifacts: snapshot.artifacts,
      ...snapshot.markerArtifacts ? { markerArtifacts: snapshot.markerArtifacts } : {},
      tombstonedIds: snapshot.tombstonedIds ?? [],
      stickyEphemeralIds: snapshot.stickyEphemeralIds ?? [],
      warnings: [...warnings, ...snapshotWarnings]
    };
  }, "restoredArtifactSnapshotFromState");
  const artifactSnapshotUnavailableReason = /* @__PURE__ */ __name((state) => {
    const reason = state.artifactSnapshotUnavailable;
    return typeof reason === "string" && reason ? reason : void 0;
  }, "artifactSnapshotUnavailableReason");
  const publicRestoreState = /* @__PURE__ */ __name((state) => {
    const {
      artifactSnapshot: _artifactSnapshot,
      artifactSnapshotUnavailable: _artifactSnapshotUnavailable,
      ...publicState
    } = state;
    return publicState;
  }, "publicRestoreState");
  async function restoreSession(action, req, options = {}) {
    if (shuttingDown) {
      throw new Error("AcpSessionBridge is shutting down");
    }
    const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
    if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
      throw new Error(
        `Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
      );
    }
    const historyReplay = action === "load" ? req.historyReplay ?? "stream" : "stream";
    const existing = byId.get(req.sessionId);
    if (existing) {
      existing.attachCount++;
      const clientId = registerClient(existing, req.clientId);
      if (req.approvalMode) {
        await applyApprovalModeForAttach(existing, req.approvalMode, clientId);
      }
      return {
        sessionId: existing.sessionId,
        workspaceCwd: existing.workspaceCwd,
        attached: true,
        clientId,
        createdAt: existing.createdAt,
        ...existing.sourceType ? { sourceType: existing.sourceType } : {},
        ...existing.sourceId !== void 0 ? { sourceId: existing.sourceId } : {},
        // 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 || historyReplay !== inFlight.historyReplay) {
        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"
        );
      }
      const clientId = registerClient(entry, req.clientId);
      if (req.approvalMode) {
        await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
      }
      return {
        ...restored,
        attached: true,
        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 admission = options.skipFreshSessionAdmission === true ? void 0 : reserveFreshSession({
      operation: action,
      workspaceCwd: workspaceKey,
      sessionId: req.sessionId
    });
    let admissionReleased = false;
    const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
      if (admissionReleased) return;
      admissionReleased = true;
      releaseFreshSessionReservation(admission);
    }, "releaseAdmissionOnce");
    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;
      let replayUpdates = [];
      let replayPartial;
      let replayError;
      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: [],
                ...historyReplay === "response" ? {
                  _meta: {
                    [LOAD_REPLAY_MODE_META_KEY]: LOAD_REPLAY_BULK_MODE
                  }
                } : {}
              }),
              initTimeoutMs,
              "loadSession"
            ),
            transportClosed
          ]);
        } else {
          state = await Promise.race([
            withTimeout(
              ci.connection.unstable_resumeSession({
                sessionId: req.sessionId,
                cwd: workspaceKey,
                mcpServers: []
              }),
              initTimeoutMs,
              "resumeSession"
            ),
            transportClosed
          ]);
        }
        if (action === "load" && historyReplay === "response") {
          const extracted = extractLoadReplayResponse(state);
          state = extracted.state;
          replayUpdates = extracted.updates;
          replayPartial = extracted.partial;
          replayError = extracted.replayError;
        }
      } 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);
        if (req.approvalMode) {
          try {
            await applyApprovalMode(
              racedEntry,
              req.approvalMode,
              false,
              clientId2
            );
          } catch (err) {
            await rollbackAttachRegistration(
              racedEntry,
              clientId2,
              1 + coalesceState.count
            );
            throw err;
          }
        }
        return {
          sessionId: racedEntry.sessionId,
          workspaceCwd: racedEntry.workspaceCwd,
          attached: true,
          clientId: clientId2,
          createdAt: racedEntry.createdAt,
          ...racedEntry.sourceType ? { sourceType: racedEntry.sourceType } : {},
          ...racedEntry.sourceId !== void 0 ? { sourceId: racedEntry.sourceId } : {},
          state: racedEntry.restoreState ?? {},
          hasActivePrompt: racedEntry.promptActive,
          ...replayFieldsFor(racedEntry, action)
        };
      }
      const entry = createSessionEntry(
        ci,
        req.sessionId,
        workspaceKey,
        restoreEvents,
        {
          drainEarlyEvents: replayUpdates.length === 0,
          lifecycleReason: action,
          // Re-seed the persisted parent lineage the caller recovered from the
          // transcript, so a restored sub-session's status reports its parent.
          ...req.parentSessionId ? { parentSessionId: req.parentSessionId } : {},
          ...req.sourceType ? { sourceType: req.sourceType } : {},
          ...req.sourceId !== void 0 ? { sourceId: req.sourceId } : {}
        }
      );
      releaseAdmissionOnce();
      const restoredArtifactSnapshot = restoredArtifactSnapshotFromState(state);
      const publicState = publicRestoreState(state);
      entry.restoreState = publicState;
      if (replayPartial === true) {
        entry.restoreReplayPartial = true;
      }
      if (replayError !== void 0) {
        entry.restoreReplayError = replayError;
      }
      seedSnapshotCaches(entry, publicState);
      const artifactRestoreWarnings = await entry.artifacts.restore(
        restoredArtifactSnapshot
      );
      for (const warning of artifactRestoreWarnings) {
        writeStderrLine(
          `[artifacts] session=${entry.sessionId} action=restore_warning warning=${JSON.stringify(
            warning
          )}`
        );
      }
      const artifactRestoreFailed = artifactRestoreWarnings.some(
        (warning) => isArtifactRestoreFailureWarning(warning)
      );
      if (replayUpdates.length > 0) {
        await ci.client.seedSessionUpdates(entry, replayUpdates, {
          ingestArtifacts: restoredArtifactSnapshot === void 0 || artifactRestoreFailed
        });
        ci.client.drainEarlyEvents(entry.sessionId, entry);
      }
      const clientId = registerClient(entry, req.clientId);
      if (req.approvalMode) {
        await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
      }
      entry.attachCount = coalesceState.count;
      registeredEntry = entry;
      return {
        sessionId: entry.sessionId,
        workspaceCwd: entry.workspaceCwd,
        attached: false,
        clientId,
        createdAt: entry.createdAt,
        ...entry.sourceType ? { sourceType: entry.sourceType } : {},
        ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
        state: publicState,
        ...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
        hasActivePrompt: entry.promptActive,
        ...replayFieldsFor(entry, action)
      };
    })().finally(async () => {
      releaseAdmissionOnce();
      ci?.pendingRestoreIds.delete(req.sessionId);
      ci?.client.clearRestoreInFlight(req.sessionId);
      pendingRestoreEvents.delete(req.sessionId);
      if (!registeredEntry) {
        restoreEvents.close();
        let removedRestoreEntry = false;
        const restoreEntry = byId.get(req.sessionId);
        if (restoreEntry?.events === restoreEvents) {
          byId.delete(req.sessionId);
          ci?.sessionIds.delete(req.sessionId);
          emitSessionLifecycle({
            type: "removed",
            sessionId: req.sessionId,
            workspaceCwd: restoreEntry.workspaceCwd,
            reason: "restore_failed"
          });
          removedRestoreEntry = true;
        }
        if (removedRestoreEntry && ci && hasNoChannelWork(ci)) {
          ci.emptyReapPending = true;
          ci.isDying = true;
        }
        ci?.client.markSessionClosed(req.sessionId);
      }
      if (ci) {
        await reapPendingEmptyChannel(ci);
      }
    });
    inFlightRestores.set(req.sessionId, {
      action,
      historyReplay,
      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();
    entry.pendingInteractions.clear();
    if (entry.promptActive) {
      entry.promptActive = false;
      activePromptCounter--;
      touchActivity();
    }
    byId.delete(sessionId);
    telemetry.metrics?.sessionLifecycle("close");
    emitSessionLifecycle({
      type: "removed",
      sessionId,
      workspaceCwd: entry.workspaceCwd,
      reason
    });
    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,
          compactedReplayMaxBytes,
          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 pendingPromptTotal() {
      let total = 0;
      for (const entry of byId.values()) {
        for (const pending of entry.pendingPromptList) {
          if (pending.state === "queued") total += 1;
        }
      }
      return total;
    },
    // Daemon Status child-resource: sync cache read for the sampler + the async
    // refresh it fires each tick to update that cache.
    getChildResourceSnapshot,
    refreshChildResource,
    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;
      const source = parseSessionSource(req.sourceType, req.sourceId);
      if ("error" in source) {
        throw new InvalidSessionMetadataError("sourceType", source.error);
      }
      if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
        throw new Error(
          `Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
        );
      }
      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(() => {
            });
          }
          if (req.approvalMode) {
            await applyApprovalModeForAttach(
              existing,
              req.approvalMode,
              clientId
            );
          }
          return {
            sessionId: existing.sessionId,
            workspaceCwd: existing.workspaceCwd,
            attached: true,
            clientId,
            createdAt: existing.createdAt,
            ...existing.sourceType ? { sourceType: existing.sourceType } : {},
            ...existing.sourceId !== void 0 ? { sourceId: existing.sourceId } : {},
            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(() => {
            });
          }
          if (req.approvalMode) {
            await applyApprovalModeForAttach(
              attachedEntry,
              req.approvalMode,
              clientId
            );
          }
          return {
            ...session,
            attached: true,
            clientId,
            hasActivePrompt: attachedEntry.promptActive
          };
        }
      }
      if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
        throw new SessionLimitExceededError(maxSessions);
      }
      const admission = reserveFreshSession({
        operation: "spawn",
        workspaceCwd: workspaceKey
      });
      let admissionReleased = false;
      const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
        if (admissionReleased) return;
        admissionReleased = true;
        releaseFreshSessionReservation(admission);
      }, "releaseAdmissionOnce");
      const promise = doSpawn(
        req.modelServiceId,
        effectiveScope,
        req.approvalMode,
        req.clientId,
        releaseAdmissionOnce,
        req.parentSessionId,
        source.sourceType,
        source.sourceId
      );
      const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
      inFlightSpawns.set(tracker, promise);
      try {
        return await promise;
      } finally {
        releaseAdmissionOnce();
        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;
                delete entry.turnError;
                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,
          recordingDegraded: entry.recordingDegraded
        }
      }), "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;
    },
    getSessionReplaySnapshot(sessionId) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      return entry.events.snapshotReplay();
    },
    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 admission = reserveFreshSession({
          operation: "branch",
          workspaceCwd: boundWorkspace,
          sourceSessionId: sessionId
        });
        let admissionReleased = false;
        const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
          if (admissionReleased) return;
          admissionReleased = true;
          releaseFreshSessionReservation(admission);
        }, "releaseAdmissionOnce");
        try {
          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
              },
              {
                skipFreshSessionAdmission: true
              }
            );
            releaseAdmissionOnce();
          } 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)
            }
          };
        } finally {
          releaseAdmissionOnce();
        }
      });
      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`
                );
              }
            }).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, context) {
      const entry = byId.get(sessionId);
      if (!entry) throw new SessionNotFoundError(sessionId);
      resolveTrustedClientId(entry, context?.clientId);
      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], {
        validationStrict: true,
        persistenceStrict: false
      });
      publishArtifactChanges(entry, result.changes, clientId);
      const warnings = [...result.warnings ?? []];
      return warnings.length > 0 ? { ...result, warnings } : 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);
      const warnings = [...result.warnings ?? []];
      return warnings.length > 0 ? { ...result, warnings } : 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 startsWorkspaceChannel = method === SERVE_CONTROL_EXT_METHODS.workspaceMcpRestart;
      const info = startsWorkspaceChannel ? await ensureChannel() : liveChannelInfo();
      if (!info) throw new SessionNotFoundError(`workspace-command:${method}`);
      try {
        const timeout = invokeOpts?.timeoutMs ?? initTimeoutMs;
        const invoke = /* @__PURE__ */ __name(() => withTimeout(
          Promise.race([
            info.connection.extMethod(method, params ?? {}),
            getChannelClosedReject(info)
          ]),
          timeout,
          method
        ), "invoke");
        const response = startsWorkspaceChannel ? await withWorkspaceControl(info, invoke) : await invoke();
        if (method === SERVE_CONTROL_EXT_METHODS.workspaceMcpRestart && typeof params?.["serverName"] === "string") {
          invalidateWorkspaceMcpDetailCache(params["serverName"]);
          await requestWorkspaceStatus(
            SERVE_STATUS_EXT_METHODS.workspaceMcp,
            () => {
              throw new BridgeChannelClosedError(
                "workspace MCP restart status refresh"
              );
            },
            {},
            /* @__PURE__ */ new Set([params["serverName"]])
          );
        }
        return response;
      } finally {
        if (startsWorkspaceChannel && hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace MCP restart");
        }
      }
    },
    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) {
      const result = await requestWorkspaceStatus(
        SERVE_STATUS_EXT_METHODS.workspaceMcpTools,
        () => {
          const cached = workspaceMcpToolsCache.get(serverName);
          return cached ? { ...cached, acpChannelLive: false } : {
            v: STATUS_SCHEMA_VERSION,
            workspaceCwd: boundWorkspace,
            serverName,
            initialized: false,
            acpChannelLive: false,
            tools: [],
            errors: [
              {
                kind: "mcp_tools",
                status: "not_started",
                hint: "initialize MCP discovery to populate"
              }
            ]
          };
        },
        { serverName }
      );
      if (result.acpChannelLive) {
        workspaceMcpToolsCache.set(serverName, result);
      }
      return result;
    },
    async getWorkspaceMcpResourcesStatus(serverName) {
      const result = await requestWorkspaceStatus(
        SERVE_STATUS_EXT_METHODS.workspaceMcpResources,
        () => {
          const cached = workspaceMcpResourcesCache.get(serverName);
          return cached ? { ...cached, acpChannelLive: false } : {
            v: STATUS_SCHEMA_VERSION,
            workspaceCwd: boundWorkspace,
            serverName,
            initialized: false,
            acpChannelLive: false,
            resources: [],
            errors: [
              {
                kind: "mcp_resources",
                status: "not_started",
                hint: "initialize MCP discovery to populate"
              }
            ]
          };
        },
        { serverName }
      );
      if (result.acpChannelLive) {
        workspaceMcpResourcesCache.set(serverName, result);
      }
      return result;
    },
    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 getSessionTranscriptPage(req) {
      const info = await ensureChannel();
      try {
        const response = await withWorkspaceControl(
          info,
          () => withTimeout(
            Promise.race([
              info.connection.extMethod(
                SERVE_STATUS_EXT_METHODS.sessionTranscript,
                { ...req, cwd: boundWorkspace }
              ),
              getChannelClosedReject(info)
            ]),
            Math.max(initTimeoutMs, SESSION_TRANSCRIPT_TIMEOUT_MS),
            SERVE_STATUS_EXT_METHODS.sessionTranscript
          )
        );
        return response;
      } catch (err) {
        if (isAcpSessionResourceNotFound(err, req.sessionId)) {
          throw new SessionNotFoundError(req.sessionId);
        }
        throw err;
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "session transcript");
        }
      }
    },
    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 {
            let inFlight = inFlightExtensionRefreshes.get(entry.sessionId);
            if (!inFlight || inFlight.connection !== entry.connection) {
              const promise = (async () => {
                await entry.connection.extMethod(
                  SERVE_CONTROL_EXT_METHODS.workspaceExtensionsRefresh,
                  { sessionId: entry.sessionId }
                );
              })();
              inFlight = { connection: entry.connection, promise };
              inFlightExtensionRefreshes.set(entry.sessionId, inFlight);
              const clear = /* @__PURE__ */ __name(() => {
                if (inFlightExtensionRefreshes.get(entry.sessionId) === inFlight) {
                  inFlightExtensionRefreshes.delete(entry.sessionId);
                }
              }, "clear");
              void promise.then(clear, clear);
            }
            await Promise.race([
              withTimeout(
                inFlight.promise,
                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
      );
      return await applyApprovalMode(
        entry,
        mode,
        opts2.persist,
        originatorClientId
      );
    },
    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
      };
    },
    generateSessionContent(sessionId, prompt, 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);
      resolveTrustedClientId(entry, context?.clientId);
      const requestId = randomUUID2();
      const queue = new GenerationStreamQueue(
        GENERATION_STREAM_QUEUE_CAPACITY
      );
      const request = {
        sessionId,
        connection: entry.connection,
        queue,
        settled: false
      };
      generationRequests.set(requestId, request);
      const cancel = /* @__PURE__ */ __name(() => {
        if (request.settled) return;
        request.settled = true;
        generationRequests.delete(requestId);
        queue.close();
        void entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionGenerationCancel, {
          sessionId,
          requestId
        }).catch(() => void 0);
      }, "cancel");
      signal.addEventListener("abort", cancel, { once: true });
      if (signal.aborted) {
        cancel();
        return queue;
      }
      void Promise.race([
        withTimeout(
          entry.connection.extMethod(
            SERVE_CONTROL_EXT_METHODS.sessionGenerationStart,
            { sessionId, requestId, prompt }
          ),
          SESSION_GENERATION_TIMEOUT_MS,
          SERVE_CONTROL_EXT_METHODS.sessionGenerationStart
        ),
        getTransportClosedReject(entry)
      ]).then((raw) => {
        if (request.settled) return;
        const response = raw;
        const model = response["model"];
        const modelSource = response["modelSource"];
        if (typeof model !== "string" || modelSource !== "fast" && modelSource !== "main") {
          throw new Error("Malformed generation completion");
        }
        const accepted = queue.push({
          type: "done",
          requestId,
          model,
          modelSource,
          ...typeof response["inputTokens"] === "number" ? { inputTokens: response["inputTokens"] } : {},
          ...typeof response["outputTokens"] === "number" ? { outputTokens: response["outputTokens"] } : {}
        });
        if (accepted) queue.close();
        else queue.fail(new Error("Generation stream consumer too slow"));
      }).catch((error) => {
        if (!request.settled) queue.fail(error);
      }).finally(() => {
        request.settled = true;
        signal.removeEventListener("abort", cancel);
        generationRequests.delete(requestId);
      });
      return queue;
    },
    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"] ?? [];
      const artifactSnapshot = restoredArtifactSnapshotFromState(
        response
      );
      const artifactSnapshotUnavailable = artifactSnapshotUnavailableReason(
        response
      );
      const beforeArtifacts = (await entry.artifacts.list()).artifacts;
      const shouldRestoreArtifactSnapshot = artifactSnapshot !== void 0 && artifactSnapshotUnavailable === void 0;
      const artifactRestoreWarnings = artifactSnapshotUnavailable !== void 0 ? [
        `artifact snapshot rebuild unavailable during rewind: ${artifactSnapshotUnavailable}`
      ] : shouldRestoreArtifactSnapshot ? await entry.artifacts.restore(artifactSnapshot, {
        preserveLiveEphemeral: true
      }) : [];
      const artifactRestoreFailed = artifactRestoreWarnings.some(
        isArtifactRestoreFailureWarning
      );
      const shouldRecordArtifactSnapshot = shouldRestoreArtifactSnapshot && !artifactRestoreFailed;
      const artifactSnapshotWarnings = shouldRecordArtifactSnapshot ? await entry.artifacts.recordSnapshot() : [];
      const artifactWarnings = [
        ...artifactRestoreWarnings,
        ...artifactSnapshotWarnings
      ];
      for (const warning of artifactRestoreWarnings) {
        writeStderrLine(
          `[artifacts] session=${entry.sessionId} action=rewind_restore_warning warning=${JSON.stringify(
            warning
          )}`
        );
      }
      for (const warning of artifactSnapshotWarnings) {
        writeStderrLine(
          `[artifacts] session=${entry.sessionId} action=rewind_snapshot_warning warning=${JSON.stringify(
            warning
          )}`
        );
      }
      const afterArtifacts = (await entry.artifacts.list()).artifacts;
      publishArtifactChanges(
        entry,
        artifactReseedChanges(beforeArtifacts, afterArtifacts),
        originatorClientId
      );
      try {
        entry.events.publish({
          type: "session_rewound",
          data: {
            sessionId,
            promptId: req.promptId,
            targetTurnIndex,
            filesChanged,
            filesFailed,
            ...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
          },
          ...originatorClientId ? { originatorClientId } : {}
        });
      } catch {
      }
      return {
        rewound: filesFailed.length === 0,
        targetTurnIndex,
        filesChanged,
        filesFailed,
        ...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
      };
    },
    async manageMcpServer(serverName, action, originatorClientId) {
      const info = await ensureChannel();
      try {
        return await withWorkspaceControl(info, async () => {
          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)
          ]);
          if (action === "authenticate" && response.pending) {
            info.workspaceMcpAuthenticationServerNames.add(serverName);
            const previousTimer = info.workspaceMcpAuthenticationTimers.get(serverName);
            if (previousTimer) clearTimeout(previousTimer);
            const timer = setTimeout(() => {
              info.workspaceMcpAuthenticationServerNames.delete(serverName);
              info.workspaceMcpAuthenticationTimers.delete(serverName);
              if (hasNoChannelWork(info)) {
                void startIdleTimer(
                  info,
                  "workspace MCP authentication timeout"
                );
              }
            }, MCP_OAUTH_TIMEOUT_MS);
            timer.unref();
            info.workspaceMcpAuthenticationTimers.set(serverName, timer);
          }
          invalidateWorkspaceMcpDetailCache(serverName);
          await requestWorkspaceStatus(
            SERVE_STATUS_EXT_METHODS.workspaceMcp,
            () => {
              throw new BridgeChannelClosedError(
                "workspace MCP management status refresh"
              );
            },
            {},
            /* @__PURE__ */ new Set([serverName])
          );
          broadcastWorkspaceEvent({
            type: "mcp_server_changed",
            data: {
              serverName: response.serverName,
              action: response.action,
              originatorClientId
            },
            ...originatorClientId ? { originatorClientId } : {}
          });
          return response;
        });
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace MCP management");
        }
      }
    },
    async initializeWorkspaceMcp() {
      const info = await ensureChannel();
      info.workspaceMcpDiscoveryRequested = true;
      try {
        const result = await Promise.race([
          withTimeout(
            info.connection.extMethod(
              SERVE_CONTROL_EXT_METHODS.workspaceMcpInitialize,
              { cwd: boundWorkspace }
            ),
            initTimeoutMs,
            SERVE_CONTROL_EXT_METHODS.workspaceMcpInitialize
          ),
          getChannelClosedReject(info)
        ]);
        if (result.accepted) {
          beginWorkspaceMcpDiscovery(info);
        }
        return result;
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace MCP initialization");
        }
      }
    },
    async reloadWorkspaceMcp() {
      const info = await ensureChannel();
      info.workspaceMcpDiscoveryRequested = true;
      try {
        const result = await Promise.race([
          withTimeout(
            info.connection.extMethod(
              SERVE_CONTROL_EXT_METHODS.workspaceMcpReload,
              { cwd: boundWorkspace }
            ),
            initTimeoutMs,
            SERVE_CONTROL_EXT_METHODS.workspaceMcpReload
          ),
          getChannelClosedReject(info)
        ]);
        if (result.accepted) {
          beginWorkspaceMcpDiscovery(info);
        }
        return result;
      } finally {
        if (hasNoChannelWork(info)) {
          await startIdleTimer(info, "workspace MCP reload");
        }
      }
    },
    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 false;
      if (opts2?.requireZeroAttaches && entry.attachCount > 0) {
        entry.spawnOwnerWantedKill = true;
        return false;
      }
      permissionMediator.forgetSession(sessionId);
      entry.pendingPermissionIds.clear();
      entry.pendingInteractions.clear();
      if (entry.promptActive) {
        entry.promptActive = false;
        activePromptCounter--;
        touchActivity();
      }
      if (defaultEntry === entry) defaultEntry = void 0;
      byId.delete(sessionId);
      telemetry.metrics?.sessionLifecycle("die");
      emitSessionLifecycle({
        type: "removed",
        sessionId,
        workspaceCwd: entry.workspaceCwd,
        reason: "killed"
      });
      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}"`);
        }
      }
      return true;
    },
    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);
      const entries = Array.from(byId.values());
      defaultEntry = void 0;
      byId.clear();
      for (const entry of entries) {
        emitSessionLifecycle({
          type: "removed",
          sessionId: entry.sessionId,
          workspaceCwd: entry.workspaceCwd,
          reason: "kill_all"
        });
      }
      for (const info of channels) {
        try {
          info.channel.killSync();
        } catch {
        }
      }
    },
    shutdown(options) {
      if (shutdownPromise) return shutdownPromise;
      const shutdownReason = options?.reason ?? "daemon_shutdown";
      let resolveShutdown;
      let rejectShutdown;
      shutdownPromise = new Promise((resolve2, reject) => {
        resolveShutdown = resolve2;
        rejectShutdown = reject;
      });
      void (async () => {
        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();
          e.pendingInteractions.clear();
        }
        defaultEntry = void 0;
        byId.clear();
        for (const e of entries) {
          telemetry.metrics?.sessionLifecycle("die");
          emitSessionLifecycle({
            type: "removed",
            sessionId: e.sessionId,
            workspaceCwd: e.workspaceCwd,
            reason: shutdownReason
          });
          try {
            e.events.publish({
              type: "session_died",
              data: { sessionId: e.sessionId, reason: shutdownReason }
            });
          } 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
        ]);
      })().then(resolveShutdown, rejectShutdown);
      return shutdownPromise;
    },
    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,
  SessionArtifactAuthorizationError,
  parseSessionSource,
  extractErrorMessage,
  extractErrorCode,
  classifyTurnErrorKind,
  createAcpSessionBridge,
  createHttpAcpBridge
};
/**
 * @license
 * Copyright 2025 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
