import { AudioFrame } from "@livekit/rtc-node";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { APIConnectionError, APIError } from "../../_exceptions.js";
import { log } from "../../log.js";
import { DEFAULT_API_CONNECT_OPTIONS } from "../../types.js";
import {
} from "./base.js";
import { TurnDetector, TurnDetectorStreamImpl } from "./detector.js";
import { LOCAL_LANGUAGES, ThresholdOptions } from "./languages.js";
import { EOT_INFERENCE_METHOD } from "./runner.js";
import { LocalTransport } from "./transports.js";
const SERVER_THRESHOLDS = { en: 0.56, ja: 0.37, fr: 0.575 };
const SERVER_DEFAULT_THRESHOLD = 0.5;
const SERVER_BACKCHANNEL_THRESHOLDS = { en: 0.62, ja: 0.7 };
const SERVER_BACKCHANNEL_DEFAULT = 0.6;
async function waitFor(predicate, ticks = 50) {
  for (let i = 0; i < ticks; i++) {
    if (predicate()) return;
    await new Promise((r) => setImmediate(r));
  }
}
class ScriptedTransport {
  runBehavior;
  runExc;
  runCalls = 0;
  events = [];
  _stream;
  constructor(opts = {}) {
    this.runBehavior = opts.runBehavior ?? "idle";
    this.runExc = opts.runExc;
  }
  attach(stream) {
    this._stream = stream;
  }
  async run() {
    this.runCalls += 1;
    if (this.runBehavior === "raise") {
      if (!this.runExc) throw new Error("runExc not set");
      throw this.runExc;
    }
    if (this.runBehavior === "return") {
      return;
    }
    await new Promise(() => void 0);
  }
  runInference(requestId) {
    this.events.push(["run_inference", requestId]);
  }
  async pushFrame(frame) {
    this.events.push(["push_frame", frame]);
  }
  async flush(sentinel) {
    this.events.push(["flush", sentinel]);
  }
  detach() {
    this.events.push(["detach", null]);
  }
}
function detectorOpts(detector) {
  return detector._opts;
}
function makeStreamWithTransport(transport, opts = {}) {
  const model = opts.model ?? "turn-detector-v1";
  const detector = opts.detector ?? makeMockDetector(model, opts.userThreshold);
  const stream = new TurnDetectorStreamImpl({
    detector,
    opts: detectorOpts(detector),
    cloudOpts: model === "turn-detector-v1" ? {
      baseUrl: "ws://test",
      apiKey: "x",
      apiSecret: "x",
      connOptions: DEFAULT_API_CONNECT_OPTIONS
    } : void 0,
    model,
    transport
  });
  return stream;
}
function makeMockDetector(model, userThreshold) {
  const originalEnv = { ...process.env };
  if (model === "turn-detector-v1-mini") {
    delete process.env.LIVEKIT_REMOTE_EOT_URL;
  } else {
    process.env.LIVEKIT_REMOTE_EOT_URL = "ws://test";
    process.env.LIVEKIT_API_KEY = "x";
    process.env.LIVEKIT_API_SECRET = "x";
  }
  const det = new TurnDetector();
  process.env = originalEnv;
  const internals = det;
  internals._model = model;
  internals._opts = { ...internals._opts, thresholds: new ThresholdOptions(model, userThreshold) };
  return det;
}
function withEnv(overrides, fn) {
  const original = { ...process.env };
  for (const [k, v] of Object.entries(overrides)) {
    if (v === void 0) delete process.env[k];
    else process.env[k] = v;
  }
  try {
    const result = fn();
    if (result instanceof Promise) {
      return result.finally(() => {
        process.env = original;
      });
    }
    process.env = original;
    return result;
  } catch (err) {
    process.env = original;
    throw err;
  }
}
let runSpy;
beforeEach(() => {
  runSpy = vi.spyOn(LocalTransport.prototype, "run").mockImplementation(async () => void 0);
});
afterEach(() => {
  runSpy.mockRestore();
});
describe("AutoSelect", () => {
  it("selects local when no remote EOT url", () => {
    void withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
      const detector = new TurnDetector();
      expect(detector.model).toBe("turn-detector-v1-mini");
    });
  });
  it("selects cloud when remote EOT url set", () => {
    void withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: "k",
        LIVEKIT_API_SECRET: "s"
      },
      () => {
        const detector = new TurnDetector();
        expect(detector.model).toBe("turn-detector-v1");
      }
    );
  });
  it("downgrades to local when creds missing", () => {
    void withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: void 0,
        LIVEKIT_API_SECRET: void 0,
        LIVEKIT_INFERENCE_API_KEY: void 0,
        LIVEKIT_INFERENCE_API_SECRET: void 0
      },
      () => {
        const detector = new TurnDetector();
        expect(detector.model).toBe("turn-detector-v1-mini");
      }
    );
  });
});
describe("ExplicitModelErrors", () => {
  it("explicit cloud missing creds throws", () => {
    void withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: void 0,
        LIVEKIT_API_KEY: void 0,
        LIVEKIT_API_SECRET: void 0,
        LIVEKIT_INFERENCE_API_KEY: void 0,
        LIVEKIT_INFERENCE_API_SECRET: void 0
      },
      () => {
        expect(() => new TurnDetector({ version: "v1" })).toThrow();
      }
    );
  });
});
describe("Fallback", () => {
  it("fallback on transport error swaps to local", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(stream.model).toBe("turn-detector-v1-mini");
    expect(stream.isFallback).toBe(true);
    expect(stream.warnedCloudFailure).toBe(true);
    expect(transport.events).toContainEqual(["detach", null]);
    await stream.aclose();
  });
  it("fallback on timed-out cancelInference", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    const fut = stream.predict();
    stream.cancelInference({ timedOut: true });
    expect((await fut.await).endOfTurnProbability).toBe(0);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(stream.model).toBe("turn-detector-v1-mini");
    expect(stream.isFallback).toBe(true);
    await stream.aclose();
  });
  it("fallback persists across turns", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(transport.runCalls).toBe(1);
    stream.predict();
    expect(stream.model).toBe("turn-detector-v1-mini");
    await stream.aclose();
  });
});
describe("MultiStreamOwnership", () => {
  it("multiple streams can be opened off one detector", async () => {
    let detector;
    withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
      detector = new TurnDetector({ version: "v1-mini" });
    });
    const s1 = detector.stream();
    const s2 = detector.stream();
    await s1.aclose();
    await s2.aclose();
  });
});
describe("DetectorViewAfterFallback", () => {
  it("detector model + threshold follow the fallback (shared ThresholdOptions)", async () => {
    let detector;
    withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: "k",
        LIVEKIT_API_SECRET: "s"
      },
      () => {
        detector = new TurnDetector({ unlikelyThreshold: 0.5 });
      }
    );
    expect(detector.model).toBe("turn-detector-v1");
    expect(await detector.unlikelyThreshold("en")).toBeCloseTo(0.5);
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = new TurnDetectorStreamImpl({
      detector,
      opts: detectorOpts(detector),
      cloudOpts: void 0,
      model: "turn-detector-v1",
      transport
    });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(stream.model).toBe("turn-detector-v1-mini");
    expect(detector.model).toBe("turn-detector-v1-mini");
    const expected = LOCAL_LANGUAGES.en * (0.5 / SERVER_THRESHOLDS.en);
    expect(await detector.unlikelyThreshold("en")).toBeCloseTo(expected);
    await stream.aclose();
  });
});
describe("LocalFailureRetry", () => {
  it("local failure emits default and retries on next turn", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new Error("local boom")
    });
    const stream = makeStreamWithTransport(transport, { model: "turn-detector-v1-mini" });
    await waitFor(() => stream.warnedLocalFailure);
    expect(stream.model).toBe("turn-detector-v1-mini");
    expect(stream.isFallback).toBe(false);
    expect(stream.warnedLocalFailure).toBe(true);
    expect(stream.transport).toBe(transport);
    await stream.aclose();
  });
});
describe("WarningDedupe", () => {
  it("cloud\u2192local warning logged once per session", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    stream._fallBackToLocal(new APIConnectionError({ message: "boom2" }));
    expect(stream.warnedCloudFailure).toBe(true);
    await stream.aclose();
  });
  it("local warning logged once per session", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { model: "turn-detector-v1-mini" });
    stream._onLocalFailure(new Error("a"));
    stream._onLocalFailure(new Error("b"));
    expect(stream.warnedLocalFailure).toBe(true);
    await stream.aclose();
  });
});
describe("ResolveThresholds", () => {
  function cloud(overrides) {
    const opts = new ThresholdOptions("turn-detector-v1", overrides);
    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    return opts;
  }
  it("no override adopts server map + fallback default", () => {
    const opts = cloud();
    expect(opts.thresholds).toEqual(SERVER_THRESHOLDS);
    expect(opts.defaultThreshold).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);
  });
  it("scalar override replaces with empty map", () => {
    const opts = cloud(0.8);
    expect(opts.thresholds).toEqual({});
    expect(opts.defaultThreshold).toBeCloseTo(0.8);
  });
  it("dict override layers on server map", () => {
    const opts = cloud({ en: 0.7 });
    expect(opts.thresholds.en).toBeCloseTo(0.7);
    expect(opts.thresholds.ja).toBeCloseTo(SERVER_THRESHOLDS.ja);
    expect(opts.defaultThreshold).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);
  });
  it("dict keys normalized", () => {
    const opts = cloud({ English: 0.7, "en-US": 0.7 });
    expect(opts.thresholds.en).toBeCloseTo(0.7);
  });
});
describe("BackchannelThresholds", () => {
  function cloud() {
    const opts = new ThresholdOptions("turn-detector-v1");
    opts._updateDefaults(
      { ...SERVER_THRESHOLDS },
      SERVER_DEFAULT_THRESHOLD,
      { ...SERVER_BACKCHANNEL_THRESHOLDS },
      SERVER_BACKCHANNEL_DEFAULT
    );
    return opts;
  }
  it("lookup per-language and default", () => {
    const opts = cloud();
    expect(opts.lookupBackchannel("en")).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en);
    expect(opts.lookupBackchannel("de")).toBeCloseTo(SERVER_BACKCHANNEL_DEFAULT);
    expect(opts.lookupBackchannel(void 0)).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en);
  });
  it("disabled when server omits backchannel", () => {
    const opts = new ThresholdOptions("turn-detector-v1");
    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    expect(opts.lookupBackchannel("en")).toBeUndefined();
  });
  it("disabled for the local mini model", () => {
    const opts = new ThresholdOptions("turn-detector-v1-mini");
    expect(opts.lookupBackchannel("en")).toBeUndefined();
  });
  it("non-positive threshold treated as disabled", () => {
    const opts = new ThresholdOptions("turn-detector-v1");
    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD, { en: 0 }, 0.6);
    expect(opts.lookupBackchannel("en")).toBeUndefined();
    expect(opts.lookupBackchannel("de")).toBeCloseTo(0.6);
  });
  it("cleared on local fallback", () => {
    const opts = cloud();
    opts._toLocalFallback();
    expect(opts.lookupBackchannel("en")).toBeUndefined();
  });
});
describe("ResolveBackchannelThresholds", () => {
  function cloud(overrides) {
    const opts = new ThresholdOptions("turn-detector-v1", void 0, overrides);
    opts._updateDefaults(
      { ...SERVER_THRESHOLDS },
      SERVER_DEFAULT_THRESHOLD,
      { ...SERVER_BACKCHANNEL_THRESHOLDS },
      SERVER_BACKCHANNEL_DEFAULT
    );
    return opts;
  }
  it("no override adopts server backchannel", () => {
    const opts = cloud();
    expect(opts.lookupBackchannel("en")).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en);
  });
  it("scalar override applies to every language", () => {
    const opts = cloud(0.8);
    expect(opts.lookupBackchannel("en")).toBeCloseTo(0.8);
    expect(opts.lookupBackchannel("ja")).toBeCloseTo(0.8);
  });
  it("dict override layers on server map", () => {
    const opts = cloud({ en: 0.5 });
    expect(opts.lookupBackchannel("en")).toBeCloseTo(0.5);
    expect(opts.lookupBackchannel("ja")).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.ja);
    expect(opts.lookupBackchannel("de")).toBeCloseTo(SERVER_BACKCHANNEL_DEFAULT);
  });
  it("dict keys normalized", () => {
    const opts = cloud({ English: 0.5 });
    expect(opts.lookupBackchannel("en")).toBeCloseTo(0.5);
  });
  it("scalar override enables before server defaults", () => {
    const opts = new ThresholdOptions("turn-detector-v1", void 0, 0.8);
    expect(opts.lookupBackchannel("en")).toBeCloseTo(0.8);
  });
  it("updateBackchannelOverrides re-resolves", () => {
    const opts = cloud();
    opts.updateBackchannelOverrides(0.45);
    expect(opts.lookupBackchannel("ja")).toBeCloseTo(0.45);
  });
});
describe("ServerDefaults", () => {
  it("cloud thresholds pending before session created", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    expect(await stream.unlikelyThreshold("en")).toBeUndefined();
    expect(await stream.supportsLanguage("en")).toBe(true);
    await stream.aclose();
  });
  it("cloud adopts server defaults", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(SERVER_THRESHOLDS.en);
    expect(await stream.unlikelyThreshold("de")).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);
    await stream.aclose();
  });
  it("dict override layers on server defaults", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: { en: 0.7, ja: 0.2 } });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(0.7);
    expect(await stream.unlikelyThreshold("ja")).toBeCloseTo(0.2);
    expect(await stream.unlikelyThreshold("fr")).toBeCloseTo(SERVER_THRESHOLDS.fr);
    await stream.aclose();
  });
  it("degenerate session created throws without override", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    expect(() => stream.thresholdsOptions._updateDefaults({}, 0)).toThrow(APIError);
    await stream.aclose();
  });
  it("degenerate session created throws even with override", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: 0.8 });
    expect(() => stream.thresholdsOptions._updateDefaults({}, 0)).toThrow(APIError);
    await stream.aclose();
  });
});
describe("OverrideWarning", () => {
  it("warns on construction with override", () => {
    const warnSpy = vi.spyOn(log(), "warn");
    try {
      withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
        new TurnDetector({ unlikelyThreshold: 0.5 });
      });
      const warned = warnSpy.mock.calls.some(
        (c) => JSON.stringify(c).includes("non-default turn detection threshold")
      );
      expect(warned).toBe(true);
    } finally {
      warnSpy.mockRestore();
    }
  });
  it("warns on construction with backchannel override", () => {
    const warnSpy = vi.spyOn(log(), "warn");
    try {
      withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
        new TurnDetector({ backchannelThreshold: 0.7 });
      });
      const warned = warnSpy.mock.calls.some(
        (c) => JSON.stringify(c).includes("non-default backchannel threshold")
      );
      expect(warned).toBe(true);
    } finally {
      warnSpy.mockRestore();
    }
  });
  it("no warning without override", () => {
    const warnSpy = vi.spyOn(log(), "warn");
    try {
      withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
        new TurnDetector();
      });
      const warned = warnSpy.mock.calls.some(
        (c) => JSON.stringify(c).includes("non-default turn detection threshold")
      );
      expect(warned).toBe(false);
    } finally {
      warnSpy.mockRestore();
    }
  });
});
describe("UpdateOptions", () => {
  it("re-resolves an active cloud stream against cached server defaults", async () => {
    let detector;
    withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: "k",
        LIVEKIT_API_SECRET: "s"
      },
      () => {
        detector = new TurnDetector();
      }
    );
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = new TurnDetectorStreamImpl({
      detector,
      opts: detectorOpts(detector),
      cloudOpts: void 0,
      model: "turn-detector-v1",
      transport
    });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(SERVER_THRESHOLDS.en);
    detector.updateOptions({ unlikelyThreshold: 0.7 });
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(0.7);
    await stream.aclose();
  });
  it("local model updateOptions", async () => {
    let detector;
    withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
      detector = new TurnDetector();
    });
    expect(detector.model).toBe("turn-detector-v1-mini");
    detector.updateOptions({ unlikelyThreshold: 0.42 });
    expect(await detector.unlikelyThreshold("en")).toBeCloseTo(0.42);
    await detector.aclose();
  });
});
describe("ThresholdRescaleOnFallback", () => {
  it("scalar override rescaled against server on fallback", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: 0.5 });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(stream.isFallback).toBe(true);
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(
      LOCAL_LANGUAGES.en * (0.5 / SERVER_THRESHOLDS.en)
    );
    await stream.aclose();
  });
  it("no override fallback uses local table", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(LOCAL_LANGUAGES.en);
    await stream.aclose();
  });
  it("dict override rescaled per language on fallback", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: { en: 0.55, ja: 0.25 } });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(stream.isFallback).toBe(true);
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(
      LOCAL_LANGUAGES.en * (0.55 / SERVER_THRESHOLDS.en)
    );
    expect(await stream.unlikelyThreshold("ja")).toBeCloseTo(
      LOCAL_LANGUAGES.ja * (0.25 / SERVER_THRESHOLDS.ja)
    );
    expect(await stream.unlikelyThreshold("fr")).toBeCloseTo(LOCAL_LANGUAGES.fr);
    await stream.aclose();
  });
  it("fallback before session created uses local table with override applied", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport, { userThreshold: 0.42 });
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    expect(stream.isFallback).toBe(true);
    expect(await stream.unlikelyThreshold("en")).toBeCloseTo(0.42);
    await stream.aclose();
  });
});
describe("LocalModelExecutor", () => {
  function pcmFrame(samples = 320) {
    return new AudioFrame(new Int16Array(samples), 16e3, 1, samples);
  }
  it("routes local predict through the injected executor (base64 PCM)", async () => {
    const doInference = vi.fn(async (method, data) => {
      expect(method).toBe(EOT_INFERENCE_METHOD);
      expect(typeof data.pcm).toBe("string");
      return { probability: 0.7, inferenceDurationMs: 5 };
    });
    const executor = { doInference };
    const detector = new TurnDetector({ version: "v1-mini", executor });
    const stream = detector.stream();
    try {
      stream.pushAudio(pcmFrame());
      const ev = await stream.predict().await;
      expect(ev.endOfTurnProbability).toBe(0.7);
      expect(doInference).toHaveBeenCalledWith(EOT_INFERENCE_METHOD, expect.anything());
    } finally {
      await stream.aclose();
    }
  });
  it("degrades to a positive default when no executor is available", async () => {
    const detector = new TurnDetector({ version: "v1-mini", executor: void 0 });
    const stream = detector.stream();
    try {
      const ev = await stream.predict().await;
      expect(ev.endOfTurnProbability).toBe(1);
    } finally {
      await stream.aclose();
    }
  });
});
//# sourceMappingURL=detector.test.js.map