{"version":3,"sources":["../src/simulation.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n  Scenario,\n  ScenarioGroup,\n  SimulationDispatch,\n  SimulationMode,\n  SimulationRun,\n  SimulationRun_Job,\n} from '@livekit/protocol';\nimport type { JobContext } from './job.js';\n\nexport {\n  Scenario,\n  ScenarioGroup,\n  SimulationDispatch,\n  SimulationMode,\n  SimulationRun,\n  SimulationRun_Job,\n};\n\n/** A pass/fail verdict for a scenario, with a human-readable reason. */\nexport interface SimulationVerdict {\n  success: boolean;\n  reason: string;\n}\n\n/** Decoded form of a Scenario's `userdata` (arbitrary JSON). On the wire it is\n * a JSON-encoded string; in a scenarios.yaml it is written as a nested mapping. */\nexport type ScenarioUserdata = { [key: string]: unknown };\n\n/** Decode a protojson `SimulationDispatch`, ignoring unknown fields so newer\n * servers can add fields without breaking older SDKs. Throws on malformed\n * input. */\nexport function parseSimulationDispatch(raw: string): SimulationDispatch {\n  return SimulationDispatch.fromJsonString(raw, { ignoreUnknownFields: true });\n}\n\n/**\n * Passed to the `onSimulationEnd` callback while running under a simulation.\n *\n * Carries two verdicts, both recorded for the run:\n * - {@link simulatorVerdict}: the simulator's verdict (its LLM judgment of the chat).\n * - {@link userVerdict}: your own veto, set via {@link fail} from richer checks\n *   (e.g. comparing mock backend state against the benchmark target in\n *   `scenario.userdata`). The effective result is the AND of the two: your\n *   check can fail a run the simulator passed, but it can never rescue one, so\n *   there is no `success()`; not calling {@link fail} leaves the simulator's\n *   verdict to stand.\n *\n * Use {@link jobContext} to reach the running session and the room.\n */\nexport class SimulationContext {\n  /** @internal */\n  _dispatch: SimulationDispatch;\n\n  #jobCtx: JobContext;\n  #run?: SimulationRun;\n  #simulatorVerdict?: SimulationVerdict;\n  #userVerdict?: SimulationVerdict;\n\n  constructor(dispatch: SimulationDispatch, jobCtx: JobContext) {\n    this._dispatch = dispatch;\n    this.#jobCtx = jobCtx;\n  }\n\n  get scenario(): Scenario {\n    return (this._dispatch.scenario ??= new Scenario());\n  }\n\n  /** How the simulated user interacts with the agent (text chat or audio).\n   * Unspecified is treated as text, since simulations predating the field\n   * were all text-only. */\n  get simulationMode(): SimulationMode {\n    if (this._dispatch.mode === SimulationMode.UNSPECIFIED) {\n      return SimulationMode.TEXT;\n    }\n    return this._dispatch.mode;\n  }\n\n  /** The run this session belongs to. Available as soon as the entrypoint runs,\n   * unlike {@link SimulationContext.simulationRun}, which the framework only fills in at the end. */\n  get simulationRunId(): string {\n    return this._dispatch.simulationRunId;\n  }\n\n  /** The run job (one scenario) behind this session, likewise available up front. */\n  get simulationJobId(): string {\n    return this._dispatch.jobId;\n  }\n\n  /** The run, or `undefined` before the simulation ends.\n   *\n   * Carries only its `id` today, so every other field reads as its protobuf\n   * default rather than the run's real state. Prefer {@link SimulationContext.simulationRunId},\n   * which is the same ID and is available from the entrypoint. */\n  get simulationRun(): SimulationRun | undefined {\n    return this.#run;\n  }\n\n  /** The simulator's verdict (its LLM judgment of the conversation). Read-only;\n   * recorded alongside your {@link userVerdict}.\n   *\n   * Only available once the simulation has ended, i.e. inside\n   * `onSimulationEnd`. Throws if accessed earlier (e.g. from the entrypoint). */\n  get simulatorVerdict(): SimulationVerdict {\n    if (this.#simulatorVerdict === undefined) {\n      throw new Error(\n        'simulatorVerdict is only available inside onSimulationEnd (after the simulation completes)',\n      );\n    }\n    return this.#simulatorVerdict;\n  }\n\n  /** The `JobContext` for this run; use it to reach the running session and\n   * the room. */\n  get jobContext(): JobContext {\n    return this.#jobCtx;\n  }\n\n  /** @internal Populate the simulator verdict / run before `onSimulationEnd`. */\n  _beginFinalize(opts: { simulatorVerdict: SimulationVerdict; run?: SimulationRun }): void {\n    this.#simulatorVerdict = opts.simulatorVerdict;\n    this.#run = opts.run;\n  }\n\n  /** The scenario's `userdata` decoded from its JSON string (`{}` if empty). */\n  userdata(): ScenarioUserdata {\n    if (!this.scenario.userdata) {\n      return {};\n    }\n    return JSON.parse(this.scenario.userdata) as ScenarioUserdata;\n  }\n\n  /** Veto this run from your own checks (e.g. final DB state diverged).\n   *\n   * The effective result is the AND of both verdicts, so this can only fail a\n   * run the simulator passed, never rescue one. The simulator's verdict is\n   * still reported. The last call wins if you call {@link fail} more than once. */\n  fail(reason = ''): void {\n    this.#userVerdict = { success: false, reason };\n  }\n\n  /** Your veto set via {@link fail}, or undefined if you didn't veto the run. */\n  get userVerdict(): SimulationVerdict | undefined {\n    return this.#userVerdict;\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,sBAOO;AAyBA,SAAS,wBAAwB,KAAiC;AACvE,SAAO,mCAAmB,eAAe,KAAK,EAAE,qBAAqB,KAAK,CAAC;AAC7E;AAgBO,MAAM,kBAAkB;AAAA;AAAA,EAE7B;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,UAA8B,QAAoB;AAC5D,SAAK,YAAY;AACjB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,IAAI,WAAqB;AACvB,WAAQ,KAAK,UAAU,aAAa,IAAI,yBAAS;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,iBAAiC;AACnC,QAAI,KAAK,UAAU,SAAS,+BAAe,aAAa;AACtD,aAAO,+BAAe;AAAA,IACxB;AACA,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA;AAAA;AAAA,EAIA,IAAI,kBAA0B;AAC5B,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA;AAAA,EAGA,IAAI,kBAA0B;AAC5B,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,gBAA2C;AAC7C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,mBAAsC;AACxC,QAAI,KAAK,sBAAsB,QAAW;AACxC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA,EAIA,IAAI,aAAyB;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,eAAe,MAA0E;AACvF,SAAK,oBAAoB,KAAK;AAC9B,SAAK,OAAO,KAAK;AAAA,EACnB;AAAA;AAAA,EAGA,WAA6B;AAC3B,QAAI,CAAC,KAAK,SAAS,UAAU;AAC3B,aAAO,CAAC;AAAA,IACV;AACA,WAAO,KAAK,MAAM,KAAK,SAAS,QAAQ;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KAAK,SAAS,IAAU;AACtB,SAAK,eAAe,EAAE,SAAS,OAAO,OAAO;AAAA,EAC/C;AAAA;AAAA,EAGA,IAAI,cAA6C;AAC/C,WAAO,KAAK;AAAA,EACd;AACF;","names":[]}