{"version":3,"sources":["../src/job.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as proto from '@livekit/protocol';\nimport type {\n  E2EEOptions,\n  LocalParticipant,\n  RemoteParticipant,\n  RemoteTrackPublication,\n  Room,\n  RtcConfiguration,\n} from '@livekit/rtc-node';\nimport { ParticipantKind, RoomEvent, TrackKind } from '@livekit/rtc-node';\nimport { ThrowsPromise } from '@livekit/throws-transformer/throws';\nimport { RoomServiceClient } from 'livekit-server-sdk';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { Logger } from 'pino';\nimport { INFERENCE_PRIORITY_HEADER } from './inference/utils.js';\nimport type { InferenceExecutor } from './ipc/inference_executor.js';\nimport { log } from './log.js';\nimport { SimulationContext, SimulationMode, parseSimulationDispatch } from './simulation.js';\nimport { setupCloudTracer, uploadSessionReport } from './telemetry/index.js';\nimport {\n  ATTRIBUTE_REDACTION_ENABLED,\n  ATTRIBUTE_SIMULATION_ENABLED,\n  ATTRIBUTE_SIMULATION_JOB_ID,\n  ATTRIBUTE_SIMULATION_RUN_ID,\n  ATTRIBUTE_SIMULATOR,\n  ATTRIBUTE_SIMULATOR_DISPATCH,\n  recordingEnabled,\n} from './types.js';\nimport { isCloud } from './utils.js';\nimport type { AgentSession, ResolvedRecordingOptions } from './voice/agent_session.js';\nimport { AgentsConsole } from './voice/console_io.js';\nimport { type SessionReport, createSessionReport, sessionReportToJSON } from './voice/report.js';\n\n/**\n * Base URL for LiveKit Cloud observability (`LIVEKIT_OBSERVABILITY_URL`, else the Cloud job URL).\n * Consumers append their endpoint path, so the override keeps its scheme, port, and any base path.\n * Invalid overrides are logged and ignored.\n */\nfunction observabilityUrl(livekitUrl: string): string | undefined {\n  const override = process.env.LIVEKIT_OBSERVABILITY_URL;\n  if (override) {\n    try {\n      new URL(override);\n      return override.replace(/\\/+$/, '');\n    } catch (error) {\n      log().warn(\n        { error, url: override },\n        'invalid LIVEKIT_OBSERVABILITY_URL, falling back to the job URL',\n      );\n    }\n  }\n\n  try {\n    const url = new URL(livekitUrl);\n    return url.hostname && isCloud(url) ? `https://${url.hostname}` : undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n// AsyncLocalStorage for job context, similar to Python's contextvars\nconst jobContextStorage = new AsyncLocalStorage<JobContext<unknown>>();\n\n/**\n * Returns the current job context.\n *\n * @param required - If true (default), throws when no context is found. If false, returns undefined.\n * @throws Error if no job context is found and required is true\n */\nexport function getJobContext<ProcessUserData = Record<string, unknown>>(\n  required?: true,\n): JobContext<ProcessUserData>;\nexport function getJobContext<ProcessUserData = Record<string, unknown>>(\n  required: false,\n): JobContext<ProcessUserData> | undefined;\nexport function getJobContext<ProcessUserData = Record<string, unknown>>(\n  required = true,\n): JobContext<ProcessUserData> | undefined {\n  const ctx = jobContextStorage.getStore();\n  if (!ctx && required) {\n    throw new Error('no job context found, are you running this code inside a job entrypoint?');\n  }\n  return ctx as JobContext<ProcessUserData> | undefined;\n}\n\n/**\n * Runs a function within a job context, similar to Python's contextvars.\n * @internal\n */\nexport function runWithJobContext<T>(context: JobContext, fn: () => T): T {\n  return jobContextStorage.run(context as JobContext<unknown>, fn);\n}\n\n/**\n * Runs an async function within a job context, similar to Python's contextvars.\n * @internal\n */\nexport function runWithJobContextAsync<T>(context: JobContext, fn: () => Promise<T>): Promise<T> {\n  return jobContextStorage.run(context as JobContext<unknown>, fn);\n}\n\n/** Which tracks, if any, should the agent automatically subscribe to? */\nexport enum AutoSubscribe {\n  SUBSCRIBE_ALL,\n  SUBSCRIBE_NONE,\n  VIDEO_ONLY,\n  AUDIO_ONLY,\n}\n\nexport type JobAcceptArguments = {\n  name: string;\n  identity: string;\n  metadata: string;\n  attributes?: { [key: string]: string };\n};\n\nexport type RunningJobInfo = {\n  acceptArguments: JobAcceptArguments;\n  job: proto.Job;\n  url: string;\n  token: string;\n  workerId: string;\n  apiKey?: string;\n  apiSecret?: string;\n  /**\n   * A locally-synthesized job that is not backed by a LiveKit room (e.g. the\n   * `console` CLI runner). Room-bound operations (`connect`, `deleteRoom`,\n   * recording uploads) become no-ops. Mirrors python `RunningJobInfo.fake_job`.\n   */\n  fakeJob?: boolean;\n};\n\n/** Attempted to add a function callback, but the function already exists. */\nexport class FunctionExistsError extends Error {\n  constructor(msg?: string) {\n    super(msg);\n    Object.setPrototypeOf(this, new.target.prototype);\n  }\n}\n\n/** The job and environment context as seen by the agent, accessible by the entrypoint function. */\nexport class JobContext<ProcessUserData = Record<string, unknown>> {\n  #proc: JobProcess<ProcessUserData>;\n  #info: RunningJobInfo;\n  #room: Room;\n  #onConnect: () => void;\n  #onShutdown: (s: string) => void;\n  /** @internal */\n  shutdownCallbacks: (() => Promise<void>)[] = [];\n  #participantEntrypoints: ((\n    job: JobContext<ProcessUserData>,\n    p: RemoteParticipant,\n  ) => Promise<void>)[] = [];\n  #participantTasks: {\n    [id: string]: {\n      callback: (job: JobContext<ProcessUserData>, p: RemoteParticipant) => Promise<void>;\n      result: Promise<void>;\n    };\n  } = {};\n  #logger: Logger;\n  #inferenceExecutor: InferenceExecutor;\n\n  /** @internal */\n  _primaryAgentSession?: AgentSession;\n\n  /** @internal */\n  _sessionDirectory: string;\n\n  /** @internal */\n  _redactionEnabled: boolean;\n\n  // Lazily built from the job's simulation attributes; undefined when not\n  // under a simulation. #simulationResolved guards the one-time parse.\n  #simulationCtx?: SimulationContext;\n  #simulationResolved = false;\n  /** @internal onSimulationEnd callback, injected by the job runner from the\n   * agent definition. */\n  _simulationEndFnc?: (ctx: SimulationContext) => unknown;\n\n  private connected: boolean = false;\n\n  constructor(\n    proc: JobProcess<ProcessUserData>,\n    info: RunningJobInfo,\n    room: Room,\n    onConnect: () => void,\n    onShutdown: (s: string) => void,\n    inferenceExecutor: InferenceExecutor,\n  ) {\n    this.#proc = proc;\n    this.#info = info;\n    this.#room = room;\n    this.#onConnect = onConnect;\n    this.#onShutdown = onShutdown;\n    this.onParticipantConnected = this.onParticipantConnected.bind(this);\n    this.#room.on(RoomEvent.ParticipantConnected, this.onParticipantConnected);\n    this.onParticipantDisconnected = this.onParticipantDisconnected.bind(this);\n    // Registered unconditionally: a simulator participant identifies itself by\n    // its lk.simulator attribute, and gating on simulationContext() here would\n    // miss user-token runs where no dispatch rides the job.\n    this.#room.on(RoomEvent.ParticipantDisconnected, this.onParticipantDisconnected);\n    this.#logger = log().child({\n      jobId: this.#info.job.id,\n      'lk.pii.room_name': this.#info.job.room?.name,\n    });\n    this.#inferenceExecutor = inferenceExecutor;\n    this._redactionEnabled = Boolean(info.job.enableRedaction);\n    // In console mode, recordings land in a local user-visible directory\n    // (mirrors python's AgentsConsole); real jobs use a temp dir.\n    const agentsConsole = AgentsConsole.getInstance();\n    this._sessionDirectory = agentsConsole.enabled\n      ? agentsConsole.sessionDirectory\n      : path.join(os.tmpdir(), 'livekit-agents', `job-${this.#info.job.id}`);\n  }\n\n  get proc(): JobProcess<ProcessUserData> {\n    return this.#proc;\n  }\n\n  get job(): proto.Job {\n    return this.#info.job;\n  }\n\n  /** Returns the {@link SimulationContext} when this job is running under a\n   * simulation, or `undefined` for a normal/production session.\n   *\n   * Resolved once and cached. The framework hands it to `onSimulationEnd`\n   * when the run finishes; read it earlier (e.g. from the entrypoint) to\n   * reach `scenario.userdata` for seeding mocks. Resolves synchronously from\n   * the job's `lk.simulator.dispatch` attribute (a protojson\n   * `SimulationDispatch`), available as soon as the job starts. */\n  simulationContext(): SimulationContext | undefined {\n    if (this.#simulationResolved) {\n      return this.#simulationCtx;\n    }\n    // The simulation attributes ride the agent dispatch and land on the job\n    // delivered to the agent.\n    this.#simulationResolved = true;\n    const raw = this.#info.job.attributes?.[ATTRIBUTE_SIMULATOR_DISPATCH] ?? '';\n    if (!raw) {\n      return undefined;\n    }\n\n    let dispatch;\n    try {\n      dispatch = parseSimulationDispatch(raw);\n    } catch (error) {\n      this.#logger.warn({ error }, 'failed to parse the simulation dispatch job attribute');\n      return undefined;\n    }\n    if (!dispatch.simulationRunId) {\n      this.#logger.warn('simulation dispatch attribute has no simulation_run_id, ignoring');\n      return undefined;\n    }\n\n    this.#simulationCtx = new SimulationContext(\n      dispatch,\n      this as JobContext<unknown> as JobContext,\n    );\n    return this.#simulationCtx;\n  }\n\n  /** Headers this job asserts about itself on every inference request.\n   *\n   * Merged last by `buildMetadataHeaders`, so what the job asserts about\n   * itself outranks what an individual model was configured with. Empty for\n   * an ordinary job. */\n  get inferenceHeaders(): Record<string, string> {\n    const headers: Record<string, string> = {};\n\n    // A text simulation is batch load: a run fans out many jobs at once and nobody\n    // is waiting on the answers, so it must not compete with live traffic for\n    // gateway capacity, and it must not be able to ask for priority either. Audio\n    // simulations are excluded: they run in real time against the audio pipeline,\n    // so their latency has to stay representative of production.\n    const sim = this.simulationContext();\n    if (sim !== undefined && sim.simulationMode === SimulationMode.TEXT) {\n      headers[INFERENCE_PRIORITY_HEADER] = 'low';\n    }\n\n    return headers;\n  }\n\n  get workerId(): string {\n    return this.#info.workerId;\n  }\n\n  /** @returns The room the agent was called into */\n  get room(): Room {\n    return this.#room;\n  }\n\n  get info(): RunningJobInfo {\n    return this.#info;\n  }\n\n  /** @returns Whether this job is a locally-synthesized fake job (e.g. console mode). */\n  get isFakeJob(): boolean {\n    return this.#info.fakeJob ?? false;\n  }\n\n  /** @returns The agent's participant if connected to the room, otherwise `undefined` */\n  get agent(): LocalParticipant | undefined {\n    return this.#room.localParticipant;\n  }\n\n  /** @returns The global inference executor */\n  get inferenceExecutor(): InferenceExecutor {\n    return this.#inferenceExecutor;\n  }\n\n  /**\n   * @returns The session directory for storing recordings and session data.\n   */\n  get sessionDirectory(): string {\n    return this._sessionDirectory;\n  }\n\n  /** Adds a promise to be awaited when {@link JobContext.shutdown | shutdown} is called. */\n  addShutdownCallback(callback: () => Promise<void>) {\n    this.shutdownCallbacks.push(callback);\n  }\n\n  async waitForParticipant(identity?: string): Promise<RemoteParticipant> {\n    if (!this.#room.isConnected) {\n      throw new Error('room is not connected');\n    }\n\n    for (const p of this.#room.remoteParticipants.values()) {\n      if ((!identity || p.identity === identity) && p.info.kind != ParticipantKind.AGENT) {\n        return p;\n      }\n    }\n\n    return new ThrowsPromise<RemoteParticipant, Error>((resolve, reject) => {\n      const onParticipantConnected = (participant: RemoteParticipant) => {\n        if (\n          (!identity || participant.identity === identity) &&\n          participant.info.kind != ParticipantKind.AGENT\n        ) {\n          clearHandlers();\n          resolve(participant);\n        }\n      };\n      const onDisconnected = () => {\n        clearHandlers();\n        reject(new Error('Room disconnected while waiting for participant'));\n      };\n\n      const clearHandlers = () => {\n        this.#room.off(RoomEvent.ParticipantConnected, onParticipantConnected);\n        this.#room.off(RoomEvent.Disconnected, onDisconnected);\n      };\n\n      this.#room.on(RoomEvent.ParticipantConnected, onParticipantConnected);\n      this.#room.on(RoomEvent.Disconnected, onDisconnected);\n    });\n  }\n\n  /**\n   * Connects the agent to the room.\n   *\n   * @remarks\n   * It is recommended to run this command as early in the function as possible, as executing it\n   * later may cause noticeable delay between user and agent joins.\n   *\n   * @see {@link https://github.com/livekit/node-sdks/tree/main/packages/livekit-rtc#readme |\n   * @livekit/rtc-node} for more information about the parameters.\n   */\n  async connect(\n    e2ee?: E2EEOptions,\n    autoSubscribe: AutoSubscribe = AutoSubscribe.SUBSCRIBE_ALL,\n    rtcConfig?: RtcConfiguration,\n  ) {\n    if (this.connected) {\n      return;\n    }\n\n    if (this.isFakeJob) {\n      this.#logger.debug('job_ctx.connect() is a no-op in console mode');\n      this.#onConnect();\n      this.connected = true;\n      return;\n    }\n\n    const opts = {\n      e2ee,\n      autoSubscribe: autoSubscribe == AutoSubscribe.SUBSCRIBE_ALL,\n      rtcConfig,\n      dynacast: false,\n    };\n\n    await this.#room.connect(this.#info.url, this.#info.token, opts);\n    this.#onConnect();\n\n    this.#room.remoteParticipants.forEach(this.onParticipantConnected);\n\n    if ([AutoSubscribe.AUDIO_ONLY, AutoSubscribe.VIDEO_ONLY].includes(autoSubscribe)) {\n      const desiredKind =\n        autoSubscribe === AutoSubscribe.AUDIO_ONLY ? TrackKind.KIND_AUDIO : TrackKind.KIND_VIDEO;\n      const subscribeIfMatch = (pub: RemoteTrackPublication) => {\n        if (pub.kind === desiredKind) {\n          pub.setSubscribed(true);\n        }\n      };\n\n      // Subscribe to tracks already published at connect time.\n      this.#room.remoteParticipants.forEach((p) => {\n        p.trackPublications.forEach(subscribeIfMatch);\n      });\n\n      // Server-side autosubscribe is disabled for AUDIO_ONLY/VIDEO_ONLY, so\n      // subscribe to matching tracks published AFTER connect as well (e.g. a\n      // participant that unmutes or joins later). Without this, late-published\n      // tracks are never subscribed. Mirrors Python's _apply_auto_subscribe_opts.\n      this.#room.on(RoomEvent.TrackPublished, subscribeIfMatch);\n    }\n    this.connected = true;\n  }\n\n  /** Deletes the room and disconnects all participants. */\n  async deleteRoom(roomName?: string): Promise<void> {\n    if (this.isFakeJob) {\n      this.#logger.warn('job_ctx.deleteRoom() is not executed in console mode');\n      return;\n    }\n\n    const targetRoomName = roomName ?? this.#room.name;\n    if (!targetRoomName) {\n      this.#logger.warn('cannot delete room because room name is missing');\n      return;\n    }\n\n    try {\n      const client = new RoomServiceClient(this.#info.url, this.#info.apiKey, this.#info.apiSecret);\n      await client.deleteRoom(targetRoomName);\n      this.#logger.info({ 'lk.pii.room_name': targetRoomName }, 'room deleted');\n    } catch (error) {\n      this.#logger.warn({ error, 'lk.pii.room_name': targetRoomName }, 'error while deleting room');\n    }\n  }\n\n  makeSessionReport(session?: AgentSession): SessionReport {\n    const targetSession = session || this._primaryAgentSession;\n\n    if (!targetSession) {\n      throw new Error('Cannot prepare report, no AgentSession was found');\n    }\n\n    const recorderIO = targetSession._recorderIO;\n\n    if (recorderIO && recorderIO.recording) {\n      throw new Error('Cannot create the AgentSession report, the RecorderIO is still recording');\n    }\n\n    return createSessionReport({\n      jobId: this.job.id,\n      roomId: this.job.room?.sid || '',\n      room: this.job.room?.name || '',\n      options: targetSession.sessionOptions,\n      events: targetSession._recordedEvents,\n      enableRecording: targetSession._enableRecording,\n      chatHistory: targetSession.history.copy(),\n      startedAt: targetSession._startedAt,\n      audioRecordingPath: recorderIO?.outputPath,\n      audioRecordingStartedAt: recorderIO?.recordingStartedAt,\n      modelUsage: targetSession._usageCollector.flatten(),\n    });\n  }\n\n  async _onSessionEnd(): Promise<void> {\n    const session = this._primaryAgentSession;\n    if (!session) {\n      return;\n    }\n\n    const recorderIO = session._recorderIO;\n    if (recorderIO?.recording) {\n      this.#logger.warn('recorder_io is still recording at session end, closing it');\n      await recorderIO.close();\n    }\n\n    const report = this.makeSessionReport(session);\n\n    // Console recording: dump the session report to a local file (mirrors python).\n    const agentsConsole = AgentsConsole.getInstance();\n    if (agentsConsole.enabled && agentsConsole.record) {\n      try {\n        await mkdir(this._sessionDirectory, { recursive: true });\n        await writeFile(\n          path.join(this._sessionDirectory, 'session_report.json'),\n          JSON.stringify(sessionReportToJSON(report), null, 2),\n        );\n      } catch (error) {\n        this.#logger.error({ error }, 'failed to save the session report');\n      }\n    }\n\n    // Upload session report to LiveKit Cloud if enabled. A fake job (console\n    // mode) has no backing cloud URL, so skip the upload entirely.\n    if (!this.isFakeJob) {\n      const observabilityBaseUrl = observabilityUrl(this.#info.url);\n\n      if (\n        (recordingEnabled(report.options.recordingOptions) || report.enableRecording) &&\n        observabilityBaseUrl\n      ) {\n        try {\n          await uploadSessionReport({\n            agentName: this.job.agentName,\n            observabilityUrl: observabilityBaseUrl,\n            report,\n            metadata: this._otelMetadata(report.options.recordingOptions),\n          });\n          this.#logger.info(\n            {\n              jobId: report.jobId,\n              roomId: report.roomId,\n            },\n            'Session report uploaded to LiveKit Cloud',\n          );\n        } catch (error) {\n          this.#logger.error({ error }, 'Failed to upload session report');\n        }\n      }\n    }\n\n    this.#logger.debug(\n      {\n        jobId: report.jobId,\n        roomId: report.roomId,\n        eventsCount: report.events.length,\n      },\n      'Session ended, report generated',\n    );\n\n    // Explicitly clear the recorded events to avoid leaking memory\n    session._recordedEvents = [];\n  }\n\n  /**\n   * Gracefully shuts down the job, and runs all shutdown promises.\n   *\n   * @param reason - Optional reason for shutdown\n   */\n  shutdown(reason = '') {\n    this.#onShutdown(reason);\n  }\n\n  private onParticipantDisconnected(p: RemoteParticipant): void {\n    if (!(ATTRIBUTE_SIMULATOR in (p.attributes ?? {}))) {\n      return;\n    }\n    this.#logger.debug('simulator disconnected, shutting down the job');\n    this.shutdown('simulation completed');\n  }\n\n  /** @internal */\n  onParticipantConnected(p: RemoteParticipant) {\n    for (const callback of this.#participantEntrypoints) {\n      if (this.#participantTasks[p.identity!]?.callback == callback) {\n        this.#logger.warn(\n          'a participant has joined before a prior prticipant task matching the same identity has finished:',\n          p.identity,\n        );\n      }\n      const result = callback(this, p);\n      result.finally(() => {\n        if (this.#participantTasks[p.identity!]?.result === result) {\n          delete this.#participantTasks[p.identity!];\n        }\n      });\n      this.#participantTasks[p.identity!] = { callback, result };\n    }\n  }\n\n  /**\n   * Adds a promise to be awaited whenever a new participant joins the room.\n   *\n   * @throws {@link FunctionExistsError} if an entrypoint already exists\n   */\n  addParticipantEntrypoint(\n    callback: (job: JobContext<ProcessUserData>, p: RemoteParticipant) => Promise<void>,\n  ) {\n    if (this.#participantEntrypoints.includes(callback)) {\n      throw new FunctionExistsError('entrypoints cannot be added more than once');\n    }\n\n    this.#participantEntrypoints.push(callback);\n  }\n\n  async initRecording(options: ResolvedRecordingOptions) {\n    if (this.isFakeJob) {\n      return;\n    }\n\n    const redactionEnabled = Boolean(this.job.enableRedaction || options.redaction);\n    if (redactionEnabled && options.audio && !options.transcript) {\n      throw new Error('audio upload requires transcript upload when redaction is enabled');\n    }\n    this._redactionEnabled = redactionEnabled;\n\n    const observabilityBaseUrl = observabilityUrl(this.#info.url);\n    if (!observabilityBaseUrl) {\n      return;\n    }\n\n    // The cloud tracer handles trace spans and OTel logs; only configure it\n    // when at least one of those categories is enabled.\n    if (!options.traces && !options.logs) {\n      return;\n    }\n\n    this.#logger.debug(\n      { url: observabilityBaseUrl },\n      'Configuring session recording (cloud tracer)',\n    );\n    await setupCloudTracer({\n      roomId: this.job.room!.sid,\n      jobId: this.job.id,\n      observabilityUrl: observabilityBaseUrl,\n      agentName: this.job.agentName,\n      enableTraces: options.traces,\n      enableLogs: options.logs,\n      metadata: this._otelMetadata(options),\n    });\n  }\n\n  /** @internal */\n  _otelMetadata(options?: ResolvedRecordingOptions): Record<string, boolean | string> | undefined {\n    const metadata: Record<string, boolean | string> = {};\n    const simulation = this.simulationContext();\n    if (simulation) {\n      metadata[ATTRIBUTE_SIMULATION_ENABLED] = true;\n      if (simulation.simulationRunId) {\n        metadata[ATTRIBUTE_SIMULATION_RUN_ID] = simulation.simulationRunId;\n      }\n      if (simulation.simulationJobId) {\n        metadata[ATTRIBUTE_SIMULATION_JOB_ID] = simulation.simulationJobId;\n      }\n    }\n    if (this._redactionEnabled || options?.redaction) {\n      metadata[ATTRIBUTE_REDACTION_ENABLED] = true;\n    }\n    return Object.keys(metadata).length > 0 ? metadata : undefined;\n  }\n}\n\nexport class JobProcess<UserData = Record<string, unknown>> {\n  #pid = process.pid;\n  userData = {} as UserData;\n\n  get pid(): number {\n    return this.#pid;\n  }\n}\n\n/**\n * A request sent by the server to spawn a new agent job.\n *\n * @remarks\n * For most applications, this is best left to the default, which simply accepts the job and\n * handles the logic inside the entrypoint function. This class is useful for vetting which\n * requests should fill idle processes and which should be outright rejected.\n */\nexport class JobRequest {\n  #job: proto.Job;\n  #onReject: () => Promise<void>;\n  #onAccept: (args: JobAcceptArguments) => Promise<void>;\n\n  /** @internal */\n  constructor(\n    job: proto.Job,\n    onReject: () => Promise<void>,\n    onAccept: (args: JobAcceptArguments) => Promise<void>,\n  ) {\n    this.#job = job;\n    this.#onReject = onReject;\n    this.#onAccept = onAccept;\n  }\n\n  /** @returns The ID of the job, set by the LiveKit server */\n  get id(): string {\n    return this.#job.id;\n  }\n\n  /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n  get job(): proto.Job {\n    return this.#job;\n  }\n\n  /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n  get room(): proto.Room | undefined {\n    return this.#job.room;\n  }\n\n  /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n  get publisher(): proto.ParticipantInfo | undefined {\n    return this.#job.participant;\n  }\n\n  /** @returns The agent's name, as set in {@link WorkerOptions} */\n  get agentName(): string {\n    return this.#job.agentName;\n  }\n\n  /** Rejects the job. */\n  async reject() {\n    await this.#onReject();\n  }\n\n  /** Accepts the job, launching it on an idle child process. */\n  async accept(name = '', identity = '', metadata = '', attributes?: { [key: string]: string }) {\n    if (identity === '') identity = 'agent-' + this.id;\n\n    this.#onAccept({ name, identity, metadata, attributes });\n  }\n}\n"],"mappings":"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","names":["import_utils","AutoSubscribe","_a"]}