{"version":3,"sources":["../../src/voice/chat_item_proto_coverage.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { Message } from '@bufbuild/protobuf';\nimport { AgentSession as pb } from '@livekit/protocol';\nimport { describe, expect, it } from 'vitest';\nimport {\n  AgentConfigUpdate,\n  AgentHandoffItem,\n  type ChatItem,\n  ChatMessage,\n  FunctionCall,\n  FunctionCallOutput,\n} from '../llm/chat_context.js';\nimport { encodeChatItem } from '../proto.js';\n\nconst TS = 1_700_000_000_500;\n\n// Fields the wire format declares but this SDK cannot supply yet. An entry here is a\n// deliberate parity gap; anything else missing is an accidental drop.\nconst KNOWN_GAPS = new Set([\n  // llm tps/ttfs land in livekit/agents (#6373) but were never ported to this SDK,\n  // so MetricsReport has no field to read them from.\n  'metrics.llm_node_tps',\n  'metrics.llm_node_ttfs',\n]);\n\n// Every field carries a non-default value: a proto3 scalar left at its zero value is\n// indistinguishable from one the serializer never assigned.\nconst SATURATED_ITEMS: ChatItem[] = [\n  new ChatMessage({\n    id: 'item_msg',\n    role: 'user',\n    content: ['hello'],\n    interrupted: true,\n    transcriptConfidence: 0.75,\n    extra: { key: 'value' },\n    createdAt: TS,\n    metrics: {\n      startedSpeakingAt: TS / 1000,\n      stoppedSpeakingAt: TS / 1000,\n      transcriptionDelay: 1,\n      endOfTurnDelay: 1,\n      onUserTurnCompletedDelay: 1,\n      llmNodeTtft: 1,\n      ttsNodeTtfb: 1,\n      e2eLatency: 1,\n    },\n  }),\n  new FunctionCall({ id: 'item_fc', callId: 'call-1', name: 'fn', args: '{}', createdAt: TS }),\n  new FunctionCallOutput({\n    id: 'item_fco',\n    callId: 'call-1',\n    name: 'fn',\n    output: 'ok',\n    isError: true,\n    createdAt: TS,\n  }),\n  new AgentHandoffItem({\n    id: 'item_ah',\n    oldAgentId: 'a',\n    newAgentId: 'b',\n    createdAt: TS,\n  }),\n  new AgentConfigUpdate({\n    id: 'item_acu',\n    instructions: 'be brief',\n    toolsAdded: ['x'],\n    toolsRemoved: ['y'],\n    createdAt: TS,\n  }),\n];\n\nfunction unsetFields(msg: Message, prefix = ''): string[] {\n  const json = msg.toJson() as Record<string, unknown>;\n  const missing: string[] = [];\n  for (const f of msg.getType().fields.list()) {\n    const key = f.jsonName ?? f.localName;\n    if (!(key in json)) {\n      missing.push(prefix + f.name);\n      continue;\n    }\n    // well-known types are leaves; their own fields are legitimately zero\n    if (f.kind === 'message' && !f.repeated && !f.T.typeName.startsWith('google.protobuf.')) {\n      const nested = (msg as unknown as Record<string, Message>)[f.localName];\n      missing.push(...unsetFields(nested, `${prefix}${f.name}.`));\n    }\n  }\n  return missing;\n}\n\nfunction payloadOf(item: ChatItem): { which: string; message: Message } {\n  const pbItem = encodeChatItem(item);\n  const { case: which, value } = pbItem.item;\n  if (!which) throw new Error('serializer produced an empty oneof');\n  return { which, message: value as Message };\n}\n\ndescribe('ChatItem proto coverage', () => {\n  it.each(SATURATED_ITEMS.map((item) => [item.type, item] as const))(\n    '%s reaches every proto field',\n    (_type, item) => {\n      const { which, message } = payloadOf(item);\n      const dropped = unsetFields(message).filter((f) => !KNOWN_GAPS.has(f));\n      expect(dropped, `${which} drops: ${dropped.join(', ')}`).toEqual([]);\n    },\n  );\n\n  it('detects a dropped field', () => {\n    const fco = new pb.FunctionCallOutput({\n      callId: 'call-1',\n      name: 'fn',\n      output: 'ok',\n      isError: true,\n    });\n    expect(unsetFields(fco)).toEqual(['id', 'created_at']);\n  });\n\n  it('every known gap names a real proto field', () => {\n    const declared = new Set(pb.MetricsReport.fields.list().map((f) => `metrics.${f.name}`));\n    for (const gap of KNOWN_GAPS) {\n      expect(declared, `${gap} is allowlisted but not in the proto`).toContain(gap);\n    }\n  });\n\n  it('covers every case the oneof declares', () => {\n    const declared = pb.ChatContext_ChatItem.fields.list().map((f) => f.localName);\n    const covered = SATURATED_ITEMS.map((item) => payloadOf(item).which);\n    expect(new Set(covered)).toEqual(new Set(declared));\n  });\n});\n"],"mappings":"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","names":["pb"]}