"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var remote_session_exports = {};
__export(remote_session_exports, {
  RemoteSession: () => RemoteSession,
  RoomSessionTransport: () => RoomSessionTransport,
  SessionHost: () => SessionHost,
  SessionTransport: () => SessionTransport,
  TcpSessionTransport: () => TcpSessionTransport
});
module.exports = __toCommonJS(remote_session_exports);
var import_protobuf = require("@bufbuild/protobuf");
var import_protocol = require("@livekit/protocol");
var import_throws = require("@livekit/throws-transformer/throws");
var import_events = __toESM(require("events"), 1);
var net = __toESM(require("node:net"), 1);
var import_constants = require("../constants.cjs");
var import_chat_context = require("../llm/chat_context.cjs");
var import_tool_context = require("../llm/tool_context.cjs");
var import_log = require("../log.cjs");
var import_utils = require("../utils.cjs");
var import_version = require("../version.cjs");
var import_amd = require("./amd.cjs");
var import_events2 = require("./events.cjs");
class SessionTransport {
  async start() {
  }
}
class RoomSessionTransport extends SessionTransport {
  room;
  handlerRegistered = false;
  closed = false;
  pendingMessages = [];
  waitingResolve = null;
  roomIO;
  constructor(room, roomIO) {
    super();
    this.room = room;
    this.roomIO = roomIO;
  }
  getRemoteIdentity() {
    var _a;
    return (_a = this.roomIO.linkedParticipant) == null ? void 0 : _a.identity;
  }
  async start() {
    if (this.handlerRegistered) return;
    this.room.registerByteStreamHandler(import_constants.TOPIC_SESSION_MESSAGES, this.onByteStream);
    this.handlerRegistered = true;
  }
  onByteStream = (reader, participantInfo) => {
    if (this.getRemoteIdentity() && participantInfo.identity !== this.getRemoteIdentity()) {
      return;
    }
    this.readStream(reader).catch((e) => {
      (0, import_log.log)().warn({ error: e }, "failed to read binary stream message");
    });
  };
  async readStream(reader) {
    try {
      const chunks = await reader.readAll();
      let totalLength = 0;
      for (const chunk of chunks) {
        totalLength += chunk.length;
      }
      const data = new Uint8Array(totalLength);
      let offset = 0;
      for (const chunk of chunks) {
        data.set(chunk, offset);
        offset += chunk.length;
      }
      const msg = import_protocol.AgentSession.AgentSessionMessage.fromBinary(data);
      this.enqueue(msg);
    } catch (e) {
      if (!this.closed) {
        (0, import_log.log)().warn({ error: e }, "failed to parse binary stream message");
      }
    }
  }
  async sendMessage(msg) {
    if (this.closed || !this.room.isConnected) return;
    try {
      const data = msg.toBinary();
      const opts = {
        topic: import_constants.TOPIC_SESSION_MESSAGES,
        name: (0, import_utils.shortuuid)("AS_")
      };
      const remoteIdentity = this.getRemoteIdentity();
      if (remoteIdentity) {
        opts.destinationIdentities = [remoteIdentity];
      }
      const writer = await this.room.localParticipant.streamBytes(opts);
      await writer.write(new Uint8Array(data));
      await writer.close();
    } catch (e) {
      (0, import_log.log)().warn({ error: e }, "failed to send binary stream message");
    }
  }
  async close() {
    if (this.closed) return;
    this.closed = true;
    if (this.handlerRegistered) {
      try {
        this.room.unregisterByteStreamHandler(import_constants.TOPIC_SESSION_MESSAGES);
      } catch (e) {
        (0, import_log.log)().debug({ error: e }, "byte stream handler already unregistered");
      }
      this.handlerRegistered = false;
    }
    if (this.waitingResolve) {
      this.waitingResolve({
        value: void 0,
        done: true
      });
      this.waitingResolve = null;
    }
  }
  enqueue(msg) {
    if (this.closed) return;
    if (this.waitingResolve) {
      const resolve = this.waitingResolve;
      this.waitingResolve = null;
      resolve({ value: msg, done: false });
    } else {
      this.pendingMessages.push(msg);
    }
  }
  [Symbol.asyncIterator]() {
    return {
      next: () => {
        if (this.closed && this.pendingMessages.length === 0) {
          return import_throws.ThrowsPromise.resolve({
            value: void 0,
            done: true
          });
        }
        const pending = this.pendingMessages.shift();
        if (pending) {
          return import_throws.ThrowsPromise.resolve({ value: pending, done: false });
        }
        return new import_throws.ThrowsPromise((resolve) => {
          this.waitingResolve = resolve;
        });
      },
      return: () => {
        this.close();
        return import_throws.ThrowsPromise.resolve({
          value: void 0,
          done: true
        });
      }
    };
  }
}
const TCP_HEADER_SIZE = 4;
const TCP_MAX_MESSAGE_SIZE = 1 << 20;
const TCP_DRAIN_THRESHOLD = 64 * 1024;
function waitForDrain(socket) {
  return new Promise((resolve) => {
    const done = () => {
      socket.removeListener("drain", done);
      socket.removeListener("close", done);
      socket.removeListener("error", done);
      resolve();
    };
    socket.once("drain", done);
    socket.once("close", done);
    socket.once("error", done);
  });
}
class TcpSessionTransport extends SessionTransport {
  host;
  port;
  socket = null;
  closed = false;
  constructor(host, port) {
    super();
    this.host = host;
    this.port = port;
  }
  async start() {
    await new Promise((resolve, reject) => {
      const socket = net.createConnection({ host: this.host, port: this.port }, () => {
        socket.setNoDelay(true);
        socket.removeListener("error", onConnectError);
        this.socket = socket;
        resolve();
      });
      const onConnectError = (err) => {
        socket.destroy();
        reject(err);
      };
      socket.once("error", onConnectError);
    });
  }
  async sendMessage(msg) {
    const socket = this.socket;
    if (this.closed || socket === null) return;
    const data = msg.toBinary();
    const header = Buffer.allocUnsafe(TCP_HEADER_SIZE);
    header.writeUInt32BE(data.length, 0);
    const flushed = socket.write(Buffer.concat([header, data]));
    if (!flushed && socket.writableLength > TCP_DRAIN_THRESHOLD) {
      await waitForDrain(socket);
    }
  }
  async close() {
    var _a;
    if (this.closed) return;
    this.closed = true;
    (_a = this.socket) == null ? void 0 : _a.destroy();
    this.socket = null;
  }
  async *[Symbol.asyncIterator]() {
    const socket = this.socket;
    if (socket === null) return;
    let buffer = Buffer.alloc(0);
    try {
      for await (const chunk of socket) {
        buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);
        while (buffer.length >= TCP_HEADER_SIZE) {
          const length = buffer.readUInt32BE(0);
          if (length > TCP_MAX_MESSAGE_SIZE) {
            (0, import_log.log)().error({ length }, "TCP session message too large, closing transport");
            return;
          }
          if (buffer.length < TCP_HEADER_SIZE + length) break;
          const payload = buffer.subarray(TCP_HEADER_SIZE, TCP_HEADER_SIZE + length);
          buffer = buffer.subarray(TCP_HEADER_SIZE + length);
          let msg;
          try {
            msg = import_protocol.AgentSession.AgentSessionMessage.fromBinary(payload);
          } catch (e) {
            (0, import_log.log)().warn({ error: e }, "failed to parse TCP session message");
            continue;
          }
          yield msg;
        }
      }
    } catch (e) {
      if (!this.closed) {
        (0, import_log.log)().warn({ error: e }, "TCP session transport read error");
      }
    }
  }
}
const AGENT_STATE_MAP = {
  initializing: import_protocol.AgentSession.AgentState.AS_INITIALIZING,
  idle: import_protocol.AgentSession.AgentState.AS_IDLE,
  listening: import_protocol.AgentSession.AgentState.AS_LISTENING,
  thinking: import_protocol.AgentSession.AgentState.AS_THINKING,
  speaking: import_protocol.AgentSession.AgentState.AS_SPEAKING
};
const USER_STATE_MAP = {
  speaking: import_protocol.AgentSession.UserState.US_SPEAKING,
  listening: import_protocol.AgentSession.UserState.US_LISTENING,
  away: import_protocol.AgentSession.UserState.US_AWAY
};
const AMD_CATEGORY_MAP = {
  [import_amd.AMDCategory.HUMAN]: import_protocol.AgentSession.AmdCategory.AMD_HUMAN,
  [import_amd.AMDCategory.MACHINE_IVR]: import_protocol.AgentSession.AmdCategory.AMD_MACHINE_IVR,
  [import_amd.AMDCategory.MACHINE_VM]: import_protocol.AgentSession.AmdCategory.AMD_MACHINE_VM,
  [import_amd.AMDCategory.MACHINE_UNAVAILABLE]: import_protocol.AgentSession.AmdCategory.AMD_MACHINE_UNAVAILABLE,
  [import_amd.AMDCategory.UNCERTAIN]: import_protocol.AgentSession.AmdCategory.AMD_UNCERTAIN
};
function msToTimestamp(ms) {
  return import_protobuf.Timestamp.fromDate(new Date(ms));
}
function nowTimestamp() {
  return import_protobuf.Timestamp.fromDate(/* @__PURE__ */ new Date());
}
function msToDuration(ms) {
  const wholeSeconds = Math.trunc(ms / 1e3);
  const remainderMs = ms - wholeSeconds * 1e3;
  return new import_protobuf.Duration({
    seconds: BigInt(wholeSeconds),
    nanos: Math.round(remainderMs * 1e6)
  });
}
function chatItemsToProto(items) {
  return items.filter((item) => item.type !== "agent_config_update").map(chatItemToProto);
}
function chatItemToProto(item) {
  switch (item.type) {
    case "message": {
      const msg = item;
      const roleMap = {
        developer: import_protocol.AgentSession.ChatRole.DEVELOPER,
        system: import_protocol.AgentSession.ChatRole.SYSTEM,
        user: import_protocol.AgentSession.ChatRole.USER,
        assistant: import_protocol.AgentSession.ChatRole.ASSISTANT
      };
      const content = [];
      for (const c of msg.content) {
        if (typeof c === "string") {
          content.push(new import_protocol.AgentSession.ChatMessage_ChatContent({ payload: { case: "text", value: c } }));
        } else if ((0, import_chat_context.isInstructions)(c)) {
          content.push(
            new import_protocol.AgentSession.ChatMessage_ChatContent({ payload: { case: "text", value: c.value } })
          );
        }
      }
      const metricsReport = new import_protocol.AgentSession.MetricsReport();
      if (msg.metrics.transcriptionDelay !== void 0)
        metricsReport.transcriptionDelay = msg.metrics.transcriptionDelay;
      if (msg.metrics.endOfTurnDelay !== void 0)
        metricsReport.endOfTurnDelay = msg.metrics.endOfTurnDelay;
      if (msg.metrics.onUserTurnCompletedDelay !== void 0)
        metricsReport.onUserTurnCompletedDelay = msg.metrics.onUserTurnCompletedDelay;
      if (msg.metrics.llmNodeTtft !== void 0)
        metricsReport.llmNodeTtft = msg.metrics.llmNodeTtft;
      if (msg.metrics.ttsNodeTtfb !== void 0)
        metricsReport.ttsNodeTtfb = msg.metrics.ttsNodeTtfb;
      if (msg.metrics.e2eLatency !== void 0) metricsReport.e2eLatency = msg.metrics.e2eLatency;
      const pbMsg = new import_protocol.AgentSession.ChatMessage({
        id: msg.id,
        role: roleMap[msg.role] ?? import_protocol.AgentSession.ChatRole.ASSISTANT,
        content,
        interrupted: msg.interrupted,
        metrics: metricsReport,
        createdAt: msToTimestamp(msg.createdAt)
      });
      if (msg.transcriptConfidence !== void 0) {
        pbMsg.transcriptConfidence = msg.transcriptConfidence;
      }
      return new import_protocol.AgentSession.ChatContext_ChatItem({ item: { case: "message", value: pbMsg } });
    }
    case "function_call": {
      const fc = item;
      return new import_protocol.AgentSession.ChatContext_ChatItem({
        item: {
          case: "functionCall",
          value: new import_protocol.AgentSession.FunctionCall({
            id: fc.id,
            callId: fc.callId,
            name: fc.name,
            arguments: fc.args,
            createdAt: msToTimestamp(fc.createdAt)
          })
        }
      });
    }
    case "function_call_output": {
      const fco = item;
      return new import_protocol.AgentSession.ChatContext_ChatItem({
        item: {
          case: "functionCallOutput",
          value: new import_protocol.AgentSession.FunctionCallOutput({
            id: fco.id,
            callId: fco.callId,
            name: fco.name,
            output: fco.output,
            isError: fco.isError,
            createdAt: msToTimestamp(fco.createdAt)
          })
        }
      });
    }
    case "agent_handoff": {
      const ah = item;
      return new import_protocol.AgentSession.ChatContext_ChatItem({
        item: {
          case: "agentHandoff",
          value: new import_protocol.AgentSession.AgentHandoff({
            id: ah.id,
            oldAgentId: ah.oldAgentId,
            newAgentId: ah.newAgentId,
            createdAt: msToTimestamp(ah.createdAt)
          })
        }
      });
    }
  }
}
function sessionUsageToProto(usage) {
  const modelUsages = [];
  for (const mu of usage.modelUsage) {
    switch (mu.type) {
      case "llm_usage": {
        const lu = mu;
        modelUsages.push(
          new import_protocol.AgentSession.ModelUsage({
            usage: {
              case: "llm",
              value: new import_protocol.AgentSession.LLMModelUsage({
                provider: lu.provider ?? "",
                model: lu.model ?? "",
                inputTokens: lu.inputTokens ?? 0,
                inputCachedTokens: lu.inputCachedTokens ?? 0,
                inputAudioTokens: lu.inputAudioTokens ?? 0,
                inputCachedAudioTokens: lu.inputCachedAudioTokens ?? 0,
                inputTextTokens: lu.inputTextTokens ?? 0,
                inputCachedTextTokens: lu.inputCachedTextTokens ?? 0,
                inputImageTokens: lu.inputImageTokens ?? 0,
                inputCachedImageTokens: lu.inputCachedImageTokens ?? 0,
                outputTokens: lu.outputTokens ?? 0,
                outputAudioTokens: lu.outputAudioTokens ?? 0,
                outputTextTokens: lu.outputTextTokens ?? 0,
                sessionDuration: (lu.sessionDurationMs ?? 0) / 1e3
              })
            }
          })
        );
        break;
      }
      case "tts_usage": {
        const tu = mu;
        modelUsages.push(
          new import_protocol.AgentSession.ModelUsage({
            usage: {
              case: "tts",
              value: new import_protocol.AgentSession.TTSModelUsage({
                provider: tu.provider ?? "",
                model: tu.model ?? "",
                inputTokens: tu.inputTokens ?? 0,
                outputTokens: tu.outputTokens ?? 0,
                charactersCount: tu.charactersCount ?? 0,
                audioDuration: (tu.audioDurationMs ?? 0) / 1e3
              })
            }
          })
        );
        break;
      }
      case "stt_usage": {
        const su = mu;
        modelUsages.push(
          new import_protocol.AgentSession.ModelUsage({
            usage: {
              case: "stt",
              value: new import_protocol.AgentSession.STTModelUsage({
                provider: su.provider ?? "",
                model: su.model ?? "",
                inputTokens: su.inputTokens ?? 0,
                outputTokens: su.outputTokens ?? 0,
                audioDuration: (su.audioDurationMs ?? 0) / 1e3
              })
            }
          })
        );
        break;
      }
      case "interruption_usage": {
        const iu = mu;
        modelUsages.push(
          new import_protocol.AgentSession.ModelUsage({
            usage: {
              case: "interruption",
              value: new import_protocol.AgentSession.InterruptionModelUsage({
                provider: iu.provider ?? "",
                model: iu.model ?? "",
                totalRequests: iu.totalRequests ?? 0
              })
            }
          })
        );
        break;
      }
      case "eot_usage": {
        const eu = mu;
        modelUsages.push(
          new import_protocol.AgentSession.ModelUsage({
            usage: {
              case: "eot",
              value: new import_protocol.AgentSession.EotModelUsage({
                provider: eu.provider ?? "",
                model: eu.model ?? "",
                totalRequests: eu.totalRequests ?? 0
              })
            }
          })
        );
        break;
      }
    }
  }
  return new import_protocol.AgentSession.AgentSessionUsage({ modelUsage: modelUsages });
}
function toolNames(toolCtx) {
  return (0, import_tool_context.sortedToolNames)(toolCtx);
}
function protoSerializeOptions(opts) {
  var _a, _b, _c;
  return {
    endpointing: JSON.stringify(((_a = opts.turnHandling) == null ? void 0 : _a.endpointing) ?? {}),
    interruption: JSON.stringify(((_b = opts.turnHandling) == null ? void 0 : _b.interruption) ?? {}),
    max_tool_steps: String(opts.maxToolSteps ?? 0),
    user_away_timeout: String(opts.userAwayTimeout ?? ""),
    preemptive_generation: JSON.stringify(((_c = opts.turnHandling) == null ? void 0 : _c.preemptiveGeneration) ?? {}),
    use_tts_aligned_transcript: String(opts.useTtsAlignedTranscript ?? false)
  };
}
class SessionHost {
  transport;
  audioInput;
  audioOutput;
  session;
  started = false;
  eventsRegistered = false;
  recvTask;
  tasks = /* @__PURE__ */ new Set();
  textInputCb;
  constructor(transport, audioInput, audioOutput) {
    this.transport = transport;
    this.audioInput = audioInput;
    this.audioOutput = audioOutput;
  }
  registerSession(session) {
    this.session = session;
    if (!this.eventsRegistered) {
      this.eventsRegistered = true;
      session.on(import_events2.AgentSessionEventTypes.AgentStateChanged, this.onAgentStateChanged);
      session.on(import_events2.AgentSessionEventTypes.UserStateChanged, this.onUserStateChanged);
      session.on(import_events2.AgentSessionEventTypes.ConversationItemAdded, this.onConversationItemAdded);
      session.on(import_events2.AgentSessionEventTypes.UserInputTranscribed, this.onUserInputTranscribed);
      session.on(import_events2.AgentSessionEventTypes.FunctionToolsExecuted, this.onFunctionToolsExecuted);
      session.on(import_events2.AgentSessionEventTypes.MetricsCollected, this.onMetricsCollected);
      session.on(import_events2.AgentSessionEventTypes.OverlappingSpeech, this.onOverlappingSpeech);
      session.on(import_events2.AgentSessionEventTypes.EotPrediction, this.onEotPrediction);
      session.on(import_events2.AgentSessionEventTypes.Error, this.onHostError);
      session.on(import_events2.AgentSessionEventTypes.DebugMessage, this.onDebugMessage);
    }
  }
  registerTextInput(textInputCb) {
    this.textInputCb = textInputCb;
  }
  async start() {
    if (this.started) return;
    this.started = true;
    await this.transport.start();
    this.recvTask = import_utils.Task.from(async () => this.recvLoop());
  }
  async close() {
    if (!this.started) return;
    this.started = false;
    if (this.session && this.eventsRegistered) {
      this.eventsRegistered = false;
      this.session.off(import_events2.AgentSessionEventTypes.AgentStateChanged, this.onAgentStateChanged);
      this.session.off(import_events2.AgentSessionEventTypes.UserStateChanged, this.onUserStateChanged);
      this.session.off(import_events2.AgentSessionEventTypes.ConversationItemAdded, this.onConversationItemAdded);
      this.session.off(import_events2.AgentSessionEventTypes.UserInputTranscribed, this.onUserInputTranscribed);
      this.session.off(import_events2.AgentSessionEventTypes.FunctionToolsExecuted, this.onFunctionToolsExecuted);
      this.session.off(import_events2.AgentSessionEventTypes.MetricsCollected, this.onMetricsCollected);
      this.session.off(import_events2.AgentSessionEventTypes.OverlappingSpeech, this.onOverlappingSpeech);
      this.session.off(import_events2.AgentSessionEventTypes.EotPrediction, this.onEotPrediction);
      this.session.off(import_events2.AgentSessionEventTypes.Error, this.onHostError);
      this.session.off(import_events2.AgentSessionEventTypes.DebugMessage, this.onDebugMessage);
    }
    if (this.recvTask) {
      this.recvTask.cancel();
    }
    await import_throws.ThrowsPromise.allSettled([...this.tasks].map((task) => task.cancelAndWait()));
    this.tasks.clear();
    await this.transport.close();
  }
  async recvLoop() {
    var _a, _b;
    try {
      for await (const msg of this.transport) {
        switch (msg.message.case) {
          case "request":
            if (this.session) {
              this.trackTask(
                import_utils.Task.from(
                  async () => this.handleRequestSafe(msg.message.value)
                )
              );
            }
            break;
          case "audioInput":
            (_a = this.audioInput) == null ? void 0 : _a.pushFrame(msg.message.value);
            break;
          case "audioPlaybackFinished":
            (_b = this.audioOutput) == null ? void 0 : _b.notifyPlayoutFinished();
            break;
        }
      }
    } catch (e) {
      if (this.started) {
        (0, import_log.log)().warn({ error: e }, "error processing session message");
      }
    }
  }
  sendEvent(event) {
    const msg = new import_protocol.AgentSession.AgentSessionMessage({
      message: { case: "event", value: event }
    });
    this.trackTask(import_utils.Task.from(async () => this.transport.sendMessage(msg)));
  }
  emitEvent(event, createdAt) {
    this.sendEvent(
      new import_protocol.AgentSession.AgentSessionEvent({
        createdAt: createdAt ? msToTimestamp(createdAt) : nowTimestamp(),
        event
      })
    );
  }
  onAgentStateChanged = (event) => {
    this.emitEvent(
      {
        case: "agentStateChanged",
        value: new import_protocol.AgentSession.AgentSessionEvent_AgentStateChanged({
          oldState: AGENT_STATE_MAP[event.oldState],
          newState: AGENT_STATE_MAP[event.newState]
        })
      },
      event.createdAt
    );
  };
  onUserStateChanged = (event) => {
    this.emitEvent(
      {
        case: "userStateChanged",
        value: new import_protocol.AgentSession.AgentSessionEvent_UserStateChanged({
          oldState: USER_STATE_MAP[event.oldState],
          newState: USER_STATE_MAP[event.newState]
        })
      },
      event.createdAt
    );
  };
  onUserInputTranscribed = (event) => {
    this.emitEvent(
      {
        case: "userInputTranscribed",
        value: new import_protocol.AgentSession.AgentSessionEvent_UserInputTranscribed({
          transcript: event.transcript,
          isFinal: event.isFinal
        })
      },
      event.createdAt
    );
  };
  onConversationItemAdded = (event) => {
    this.emitEvent(
      {
        case: "conversationItemAdded",
        value: new import_protocol.AgentSession.AgentSessionEvent_ConversationItemAdded({
          item: chatItemToProto(event.item)
        })
      },
      event.createdAt
    );
  };
  onFunctionToolsExecuted = (event) => {
    const pbCalls = event.functionCalls.map(
      (fc) => new import_protocol.AgentSession.FunctionCall({ name: fc.name, arguments: fc.args, callId: fc.callId })
    );
    const pbOutputs = event.functionCallOutputs.filter((fco) => fco != null).map(
      (fco) => new import_protocol.AgentSession.FunctionCallOutput({
        callId: fco.callId,
        output: fco.output,
        isError: fco.isError
      })
    );
    this.emitEvent(
      {
        case: "functionToolsExecuted",
        value: new import_protocol.AgentSession.AgentSessionEvent_FunctionToolsExecuted({
          functionCalls: pbCalls,
          functionCallOutputs: pbOutputs
        })
      },
      event.createdAt
    );
  };
  onEotPrediction = (event) => {
    this._onEotPrediction(event);
  };
  onOverlappingSpeech = (event) => {
    const value = new import_protocol.AgentSession.AgentSessionEvent_OverlappingSpeech({
      isInterruption: event.isInterruption,
      detectionDelay: event.detectionDelayInS,
      detectedAt: msToTimestamp(event.detectedAt)
    });
    if (event.overlapStartedAt != null) {
      value.overlapStartedAt = msToTimestamp(event.overlapStartedAt);
    }
    this.emitEvent({ case: "overlappingSpeech", value });
  };
  onMetricsCollected = (event) => {
    if (!this.session) return;
    this.emitEvent(
      {
        case: "sessionUsageUpdated",
        value: new import_protocol.AgentSession.AgentSessionEvent_SessionUsageUpdated({
          usage: sessionUsageToProto(this.session.usage)
        })
      },
      event.createdAt
    );
  };
  onHostError = (event) => {
    this.emitEvent(
      {
        case: "error",
        value: new import_protocol.AgentSession.AgentSessionEvent_Error({
          message: event.error ? event.error.label : "Unknown error"
        })
      },
      event.createdAt
    );
  };
  onDebugMessage = (event) => {
    this.emitEvent({ case: "debugMessage", value: event });
  };
  /**
   * @internal — forwards an AMD prediction to the connected
   * {@link RemoteSession} peer. Mirrors python
   * `SessionHost._on_amd_prediction`.
   */
  _onAmdPrediction(event) {
    this.emitEvent({
      case: "amdPrediction",
      value: new import_protocol.AgentSession.AgentSessionEvent_AmdPrediction({
        speechDuration: msToDuration(event.speechDurationMs),
        delay: msToDuration(event.delayMs),
        category: AMD_CATEGORY_MAP[event.category],
        reason: event.reason,
        transcript: event.transcript
      })
    });
  }
  /**
   * @internal — forwards an audio-EOT prediction to the connected
   * {@link RemoteSession} peer.
   */
  _onEotPrediction(event) {
    this.emitEvent({
      case: "eotPrediction",
      value: new import_protocol.AgentSession.AgentSessionEvent_EotPrediction({
        probability: event.probability,
        threshold: event.threshold,
        inferenceDuration: msToDuration(event.inferenceDurationMs),
        delay: msToDuration(event.delayMs)
      })
    });
  }
  async handleRequestSafe(req) {
    try {
      await this.handleRequest(req);
    } catch (e) {
      (0, import_log.log)().warn({ error: e, requestId: req.requestId }, "error handling session request");
      try {
        const resp = new import_protocol.AgentSession.AgentSessionMessage({
          message: {
            case: "response",
            value: new import_protocol.AgentSession.SessionResponse({
              requestId: req.requestId,
              error: "internal error"
            })
          }
        });
        await this.transport.sendMessage(resp);
      } catch (e2) {
        (0, import_log.log)().debug({ error: e2 }, "failed to send error response");
      }
    }
  }
  async handleRequest(req) {
    if (!this.session) return;
    switch (req.request.case) {
      case "ping":
        return this.sendResponse(req.requestId, {
          case: "pong",
          value: new import_protocol.AgentSession.SessionResponse_Pong()
        });
      case "getChatHistory":
        return this.handleGetChatHistory(req.requestId);
      case "getAgentInfo":
        return this.handleGetAgentInfo(req.requestId);
      case "runInput":
        return this.handleRunInput(req.requestId, req.request.value);
      case "getSessionState":
        return this.handleGetSessionState(req.requestId);
      case "getRtcStats":
        return this.sendResponse(req.requestId, {
          case: "getRtcStats",
          value: new import_protocol.AgentSession.SessionResponse_GetRTCStatsResponse({
            publisherStats: [],
            subscriberStats: []
          })
        });
      case "getSessionUsage":
        return this.handleGetSessionUsage(req.requestId);
      case "getFrameworkInfo":
        return this.sendResponse(req.requestId, {
          case: "getFrameworkInfo",
          value: new import_protocol.AgentSession.SessionResponse_GetFrameworkInfoResponse({
            sdk: "js",
            sdkVersion: import_version.version
          })
        });
      case "updateIo":
        return this.handleUpdateIo(req.requestId, req.request.value);
    }
  }
  async handleUpdateIo(requestId, update) {
    var _a;
    const session = this.session;
    if (((_a = update.input) == null ? void 0 : _a.audioEnabled) !== void 0) {
      session.input.setAudioEnabled(update.input.audioEnabled);
    }
    if (update.output) {
      if (update.output.audioEnabled !== void 0) {
        session.output.setAudioEnabled(update.output.audioEnabled);
      }
      if (update.output.transcriptionEnabled !== void 0) {
        session.output.setTranscriptionEnabled(update.output.transcriptionEnabled);
      }
    }
    return this.sendResponse(requestId, {
      case: "updateIo",
      value: new import_protocol.AgentSession.SessionResponse_UpdateIOResponse()
    });
  }
  async handleGetChatHistory(requestId) {
    const items = chatItemsToProto(this.session.history.items);
    return this.sendResponse(requestId, {
      case: "getChatHistory",
      value: new import_protocol.AgentSession.SessionResponse_GetChatHistoryResponse({ items })
    });
  }
  async handleGetAgentInfo(requestId) {
    const agent = this.session.currentAgent;
    return this.sendResponse(requestId, {
      case: "getAgentInfo",
      value: new import_protocol.AgentSession.SessionResponse_GetAgentInfoResponse({
        id: agent.id,
        instructions: (0, import_chat_context.renderInstructions)(agent.instructions),
        tools: toolNames(agent.toolCtx),
        chatCtx: chatItemsToProto(agent.chatCtx.items)
      })
    });
  }
  async handleRunInput(requestId, input) {
    const text = input.text;
    let items = [];
    let error;
    if (text) {
      if (this.textInputCb) {
        const cbResult = this.textInputCb(this.session, { text });
        if (cbResult instanceof Promise) {
          await cbResult;
        }
      } else {
        try {
          await this.session.interrupt({ force: true }).await;
        } catch {
        }
        const result = this.session.run({ userInput: text });
        try {
          await result.wait();
        } catch (e) {
          error = e instanceof Error ? e.message : String(e);
        }
        items = chatItemsToProto(result.events.map((ev) => ev.item));
      }
    }
    return this.sendResponse(
      requestId,
      {
        case: "runInput",
        value: new import_protocol.AgentSession.SessionResponse_RunInputResponse({ items })
      },
      error
    );
  }
  async handleGetSessionState(requestId) {
    const agent = this.session.currentAgent;
    const startedAt = this.session._startedAt ?? Date.now();
    return this.sendResponse(requestId, {
      case: "getSessionState",
      value: new import_protocol.AgentSession.SessionResponse_GetSessionStateResponse({
        agentState: AGENT_STATE_MAP[this.session.agentState],
        userState: USER_STATE_MAP[this.session.userState],
        agentId: agent.id,
        options: protoSerializeOptions({
          turnHandling: this.session.sessionOptions.turnHandling,
          maxToolSteps: this.session.sessionOptions.maxToolSteps,
          userAwayTimeout: this.session.sessionOptions.userAwayTimeout,
          useTtsAlignedTranscript: this.session.sessionOptions.useTtsAlignedTranscript
        }),
        createdAt: msToTimestamp(startedAt)
      })
    });
  }
  async handleGetSessionUsage(requestId) {
    return this.sendResponse(requestId, {
      case: "getSessionUsage",
      value: new import_protocol.AgentSession.SessionResponse_GetSessionUsageResponse({
        usage: sessionUsageToProto(this.session.usage),
        createdAt: nowTimestamp()
      })
    });
  }
  async sendResponse(requestId, response, error) {
    await this.transport.sendMessage(
      new import_protocol.AgentSession.AgentSessionMessage({
        message: {
          case: "response",
          value: new import_protocol.AgentSession.SessionResponse({ requestId, response, error })
        }
      })
    );
  }
  trackTask(task) {
    this.tasks.add(task);
    task.addDoneCallback(() => {
      this.tasks.delete(task);
    });
  }
}
class RemoteSession extends import_events.default {
  transport;
  started = false;
  tasks = /* @__PURE__ */ new Set();
  pendingRequests = /* @__PURE__ */ new Map();
  recvTask;
  _logger = (0, import_log.log)();
  constructor(transport) {
    super();
    this.transport = transport;
  }
  static fromRoom(room, roomIO) {
    const transport = new RoomSessionTransport(room, roomIO);
    return new RemoteSession(transport);
  }
  async start() {
    if (this.started) return;
    this.started = true;
    await this.transport.start();
    this.recvTask = import_utils.Task.from(async () => this.recvLoop());
  }
  async close() {
    if (!this.started) return;
    this.started = false;
    if (this.recvTask) {
      this.recvTask.cancel();
    }
    for (const pending of this.pendingRequests.values()) {
      pending.reject(new Error("RemoteSession closed"));
    }
    this.pendingRequests.clear();
    for (const task of this.tasks) {
      task.cancel();
    }
    this.tasks.clear();
    await this.transport.close();
  }
  async recvLoop() {
    try {
      for await (const msg of this.transport) {
        switch (msg.message.case) {
          case "event":
            this.dispatchEvent(msg.message.value);
            break;
          case "response":
            this.dispatchResponse(msg.message.value);
            break;
        }
      }
    } catch (e) {
      if (this.started) {
        this._logger.warn({ error: e }, "error in RemoteSession recv loop");
      }
    }
  }
  dispatchEvent(event) {
    const ev = event.event;
    switch (ev.case) {
      case "agentStateChanged":
        this.emit("agent_state_changed", ev.value);
        break;
      case "userStateChanged":
        this.emit("user_state_changed", ev.value);
        break;
      case "userInputTranscribed":
        this.emit("user_input_transcribed", ev.value);
        break;
      case "conversationItemAdded":
        this.emit("conversation_item_added", ev.value);
        break;
      case "functionToolsExecuted":
        this.emit("function_tools_executed", ev.value);
        break;
      case "overlappingSpeech":
        this.emit("overlapping_speech", ev.value);
        break;
      case "amdPrediction":
        this.emit("amd_prediction", ev.value);
        break;
      case "eotPrediction":
        this.emit("eot_prediction", ev.value);
        break;
      case "sessionUsageUpdated":
        this.emit("session_usage", ev.value);
        break;
      case "debugMessage":
        this.emit("debug_message", ev.value);
        break;
      case "error":
        this.emit("error", ev.value);
        break;
    }
  }
  dispatchResponse(response) {
    const future = this.pendingRequests.get(response.requestId);
    this.pendingRequests.delete(response.requestId);
    if (future && !future.done) {
      future.resolve(response);
    }
  }
  async sendRequest(buildReq, timeout = 6e4) {
    const requestId = (0, import_utils.shortuuid)("req_");
    const req = buildReq(requestId);
    req.requestId = requestId;
    const future = new import_utils.Future();
    this.pendingRequests.set(requestId, future);
    const msg = new import_protocol.AgentSession.AgentSessionMessage({
      message: { case: "request", value: req }
    });
    await this.transport.sendMessage(msg);
    const timer = setTimeout(() => {
      if (!future.done) {
        this.pendingRequests.delete(requestId);
        future.reject(new Error("RemoteSession request timed out"));
      }
    }, timeout);
    try {
      const response = await future.await;
      if (response.error) {
        throw new Error(response.error);
      }
      return response;
    } finally {
      clearTimeout(timer);
    }
  }
  async fetchSessionState() {
    const resp = await this.sendRequest(
      (id) => new import_protocol.AgentSession.SessionRequest({
        requestId: id,
        request: { case: "getSessionState", value: new import_protocol.AgentSession.SessionRequest_GetSessionState() }
      })
    );
    if (resp.response.case !== "getSessionState") {
      throw new Error("unexpected response type");
    }
    return resp.response.value;
  }
  async fetchChatHistory() {
    const resp = await this.sendRequest(
      (id) => new import_protocol.AgentSession.SessionRequest({
        requestId: id,
        request: { case: "getChatHistory", value: new import_protocol.AgentSession.SessionRequest_GetChatHistory() }
      })
    );
    if (resp.response.case !== "getChatHistory") {
      throw new Error("unexpected response type");
    }
    return resp.response.value;
  }
  async fetchAgentInfo() {
    const resp = await this.sendRequest(
      (id) => new import_protocol.AgentSession.SessionRequest({
        requestId: id,
        request: { case: "getAgentInfo", value: new import_protocol.AgentSession.SessionRequest_GetAgentInfo() }
      })
    );
    if (resp.response.case !== "getAgentInfo") {
      throw new Error("unexpected response type");
    }
    return resp.response.value;
  }
  async sendMessage(text, responseTimeout = 6e4) {
    const resp = await this.sendRequest(
      (id) => new import_protocol.AgentSession.SessionRequest({
        requestId: id,
        request: { case: "runInput", value: new import_protocol.AgentSession.SessionRequest_RunInput({ text }) }
      }),
      responseTimeout
    );
    if (resp.response.case !== "runInput") {
      throw new Error("unexpected response type");
    }
    return resp.response.value;
  }
  async fetchRtcStats() {
    const resp = await this.sendRequest(
      (id) => new import_protocol.AgentSession.SessionRequest({
        requestId: id,
        request: { case: "getRtcStats", value: new import_protocol.AgentSession.SessionRequest_GetRTCStats() }
      })
    );
    if (resp.response.case !== "getRtcStats") {
      throw new Error("unexpected response type");
    }
    return resp.response.value;
  }
  async fetchSessionUsage() {
    const resp = await this.sendRequest(
      (id) => new import_protocol.AgentSession.SessionRequest({
        requestId: id,
        request: { case: "getSessionUsage", value: new import_protocol.AgentSession.SessionRequest_GetSessionUsage() }
      })
    );
    if (resp.response.case !== "getSessionUsage") {
      throw new Error("unexpected response type");
    }
    return resp.response.value;
  }
  trackTask(task) {
    this.tasks.add(task);
    task.addDoneCallback(() => {
      this.tasks.delete(task);
    });
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  RemoteSession,
  RoomSessionTransport,
  SessionHost,
  SessionTransport,
  TcpSessionTransport
});
//# sourceMappingURL=remote_session.cjs.map