{"version":3,"sources":["../../src/voice/keyterm_detection.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { EventEmitter } from 'node:events';\nimport { beforeAll, describe, expect, it } from 'vitest';\nimport { ChatContext, ChatMessage, FunctionCall } from '../llm/chat_context.js';\nimport { LLM, type LLMStream } from '../llm/llm.js';\nimport { initializeLogger } from '../log.js';\nimport type { LLMMetrics } from '../metrics/base.js';\nimport type { SpeechEvent, SpeechStream } from '../stt/stt.js';\nimport { STT } from '../stt/stt.js';\nimport type { AudioBuffer } from '../utils.js';\nimport { Future } from '../utils.js';\nimport { createConversationItemAddedEvent } from './events.js';\nimport type { ConversationItemAddedEvent } from './events.js';\nimport {\n  type KeytermDetectionOptions,\n  KeytermDetector,\n  PENDING_TTL,\n  detectKeyterms,\n  formatInput,\n  parseToolCall,\n  resolveDetection,\n} from './keyterm_detection.js';\n\ntype DetectionResult = [string[], string[], string[]];\n\nconst detector = (opts?: {\n  staticKeyterms?: string[];\n  options?: KeytermDetectionOptions;\n}): KeytermDetector => new KeytermDetector(opts);\n\n/** Detected terms with their confirmed flag (confirmed first, then pending). */\nconst entries = (d: KeytermDetector): [string, boolean][] => [\n  ...d._detectedTerms.map((t): [string, boolean] => [t, true]),\n  ...[...d._pendingTerms.keys()].map((t): [string, boolean] => [t, false]),\n];\n\nconst ctx = (text: string = 'hello'): ChatContext => {\n  const c = ChatContext.empty();\n  c.addMessage({ role: 'user', content: text });\n  return c;\n};\n\n/** STT that records every _updateSessionKeyterms() / _pushConversationItem() call. */\nclass RecordingSTT extends STT {\n  label = 'recording.STT';\n  pushed: string[][] = [];\n  chatItems: ChatMessage[] = [];\n\n  constructor(opts?: { supportsKeyterms?: boolean; supportsChatContext?: boolean }) {\n    super({\n      streaming: true,\n      interimResults: false,\n      keyterms: opts?.supportsKeyterms ?? true,\n      chatContext: opts?.supportsChatContext ?? false,\n    });\n  }\n\n  protected _recognize(_: AudioBuffer): Promise<SpeechEvent> {\n    throw new Error('not implemented');\n  }\n\n  stream(): SpeechStream {\n    throw new Error('not implemented');\n  }\n\n  override _updateSessionKeyterms(keyterms: string[]): void {\n    this.pushed.push([...keyterms]);\n  }\n\n  override _pushConversationItem(ev: ConversationItemAddedEvent): void {\n    if (ev.item instanceof ChatMessage) {\n      this.chatItems.push(ev.item);\n    }\n  }\n}\n\nclass FakeStream {\n  private args: string;\n\n  constructor(pending: string[], confirm: string[], remove: string[]) {\n    this.args = JSON.stringify({ pending, confirm, remove });\n  }\n\n  async collect() {\n    const call = new FunctionCall({ callId: '1', name: 'record_keyterms', args: this.args });\n    return { text: '', toolCalls: [call], usage: undefined, extra: {} };\n  }\n\n  close(): void {}\n}\n\n/**\n * Fake LLM: returns a `record_keyterms` call per `chat()`, one result tuple per call.\n *\n * Subclasses LLM so the detector's `instanceof LLM` gate passes; the last result\n * repeats once the sequence is exhausted.\n */\nclass RecordingLLM extends LLM {\n  private results: DetectionResult[];\n  calls = 0;\n  lastChatCtx: ChatContext | undefined;\n\n  constructor(...results: DetectionResult[]) {\n    super();\n    this.results = results.length > 0 ? results : [[[], [], []]];\n  }\n\n  label(): string {\n    return 'recording-llm';\n  }\n\n  chat({ chatCtx }: { chatCtx: ChatContext }): LLMStream {\n    const result = this.results[Math.min(this.calls, this.results.length - 1)]!;\n    this.calls += 1;\n    this.lastChatCtx = chatCtx;\n    return new FakeStream(...result) as unknown as LLMStream;\n  }\n}\n\nclass BlockingStream extends FakeStream {\n  constructor(\n    private gate: Future,\n    result: DetectionResult = [[], [], []],\n  ) {\n    super(...result);\n  }\n\n  override async collect() {\n    await this.gate.await;\n    return super.collect();\n  }\n\n  // mirror the real LLMStream: close() ends the output queue, unblocking collect()\n  override close(): void {\n    if (!this.gate.done) {\n      this.gate.resolve();\n    }\n  }\n}\n\n/** Fake LLM whose response blocks until `gate` is resolved (for single-flight tests). */\nclass BlockingLLM extends LLM {\n  gate = new Future();\n  calls = 0;\n\n  constructor(private result: DetectionResult = [[], [], []]) {\n    super();\n  }\n\n  label(): string {\n    return 'blocking-llm';\n  }\n\n  chat(_: { chatCtx: ChatContext }): LLMStream {\n    this.calls += 1;\n    return new BlockingStream(this.gate, this.result) as unknown as LLMStream;\n  }\n}\n\nclass FakeSession extends EventEmitter {\n  history = ChatContext.empty();\n\n  addUser(text: string): void {\n    const msg = this.history.addMessage({ role: 'user', content: text });\n    this.emit('conversation_item_added', createConversationItemAddedEvent(msg));\n  }\n\n  addAssistant(text: string): void {\n    const msg = this.history.addMessage({ role: 'assistant', content: text });\n    this.emit('conversation_item_added', createConversationItemAddedEvent(msg));\n  }\n}\n\nconst drain = async (d: KeytermDetector): Promise<void> => {\n  await Promise.resolve();\n  if (d._detectTask !== undefined) {\n    // a failed pass is logged + re-raised on the task\n    await d._detectTask.result.catch(() => {});\n  }\n};\n\nbeforeAll(() => {\n  initializeLogger({ pretty: false });\n});\n\n// -- keyterm state machine (driven through one detection pass each) --\n\ndescribe('keyterm state machine', () => {\n  it('only confirmed terms are applied', async () => {\n    const d = detector({\n      staticKeyterms: ['Acme'],\n      options: { llm: new RecordingLLM([['Niamh'], ['Foo'], []]) },\n    });\n    await d.runOnce(ctx());\n    // pending terms are tracked but not applied (entries: confirmed then pending)\n    expect(entries(d)).toEqual([\n      ['Foo', true],\n      ['Niamh', false],\n    ]);\n    expect(d.keyterms).toEqual(['Acme', 'Foo']);\n  });\n\n  it('pending then confirmed', async () => {\n    const d = detector({\n      options: {\n        llm: new RecordingLLM([['Kubernetes'], [], []], [[], ['Kubernetes'], []]),\n      },\n    });\n    await d.runOnce(ctx());\n    expect(d.keyterms).toEqual([]);\n    await d.runOnce(ctx());\n    expect(entries(d)).toEqual([['Kubernetes', true]]);\n    expect(d.keyterms).toEqual(['Kubernetes']);\n  });\n\n  it('static terms shown to llm as applied', async () => {\n    const fake = new RecordingLLM();\n    const d = detector({ staticKeyterms: ['Acme Corp'], options: { llm: fake } });\n    await d.runOnce(ctx());\n    // user terms must appear in the applied list, or the LLM keeps re-proposing them\n    expect(fake.lastChatCtx).toBeDefined();\n    const items = fake.lastChatCtx!.items;\n    const lastItem = items[items.length - 1]!;\n    const userMsg = (lastItem instanceof ChatMessage ? lastItem.textContent : undefined) ?? '';\n    const appliedSection = userMsg.split('## Applied keyterms')[1]!.split('\\n')[1]!;\n    expect(appliedSection).toContain('Acme Corp');\n  });\n\n  it('user precedence and dedup', async () => {\n    const d = detector({\n      staticKeyterms: ['Acme', 'Acme', 'LiveKit'],\n      options: { llm: new RecordingLLM([[], ['LiveKit', 'Foo'], []]) },\n    });\n    expect(d.staticKeyterms).toEqual(['Acme', 'LiveKit']);\n    await d.runOnce(ctx()); // an auto term equal to a user term is dropped\n    expect(entries(d).map(([t]) => t)).toEqual(['Foo']);\n    expect(d.keyterms).toEqual(['Acme', 'LiveKit', 'Foo']);\n  });\n\n  it('confirmed cannot revert to pending', async () => {\n    const d = detector({\n      options: { llm: new RecordingLLM([[], ['Niamh'], []], [['Niamh'], [], []]) },\n    });\n    await d.runOnce(ctx());\n    expect(d.keyterms).toEqual(['Niamh']);\n    await d.runOnce(ctx()); // a stray `pending` must not reset a confirmed term\n    expect(entries(d)).toEqual([['Niamh', true]]);\n  });\n\n  it('correction removes and replaces', async () => {\n    const d = detector({\n      options: {\n        llm: new RecordingLLM([['Jon'], [], []], [['John'], [], ['Jon']], [[], ['John'], []]),\n      },\n    });\n    await d.runOnce(ctx());\n    expect(entries(d)).toEqual([['Jon', false]]);\n    await d.runOnce(ctx()); // misheard spelling removed, corrected one added as pending\n    expect(entries(d)).toEqual([['John', false]]);\n    await d.runOnce(ctx());\n    expect(d.keyterms).toEqual(['John']);\n  });\n\n  it('remove applies to confirmed terms', async () => {\n    const d = detector({\n      options: { llm: new RecordingLLM([[], ['Jon'], []], [[], ['John'], ['Jon']]) },\n    });\n    await d.runOnce(ctx());\n    expect(d.keyterms).toEqual(['Jon']);\n    await d.runOnce(ctx()); // a user correction can remove an already-applied term\n    expect(d.keyterms).toEqual(['John']);\n  });\n\n  it('remove unknown is noop', async () => {\n    const d = detector({\n      options: { llm: new RecordingLLM([[], ['Foo'], []], [[], [], ['does-not-exist']]) },\n    });\n    await d.runOnce(ctx());\n    await d.runOnce(ctx());\n    expect(d.keyterms).toEqual(['Foo']);\n  });\n\n  it('cap evicts oldest confirmed', async () => {\n    const d = detector({\n      options: { maxKeyterms: 3, llm: new RecordingLLM([[], ['a', 'b', 'c', 'd', 'e'], []]) },\n    });\n    await d.runOnce(ctx());\n    expect(entries(d).map(([t]) => t)).toEqual(['c', 'd', 'e']);\n  });\n\n  it('pending evicted when not confirmed', async () => {\n    // pass 1 adds \"Tmp\" pending; later passes never confirm it, so it ages out\n    const d = detector({\n      options: { llm: new RecordingLLM([['Tmp'], [], []], [[], ['Other'], []]) },\n    });\n    await d.runOnce(ctx());\n    for (let i = 0; i < PENDING_TTL - 1; i++) {\n      await d.runOnce(ctx());\n    }\n    expect(entries(d).map(([t]) => t)).toContain('Tmp');\n    await d.runOnce(ctx()); // TTL exceeded\n    expect(entries(d).map(([t]) => t)).not.toContain('Tmp');\n  });\n\n  it('confirmed not evicted by staleness', async () => {\n    const d = detector({\n      options: { llm: new RecordingLLM([[], ['Keep'], []], [['x'], [], []]) },\n    });\n    await d.runOnce(ctx());\n    for (let i = 0; i < PENDING_TTL + 2; i++) {\n      await d.runOnce(ctx()); // pending churn ages out, but the confirmed term stays\n    }\n    expect(d.keyterms).toEqual(['Keep']);\n  });\n\n  it('failed pass keeps state', async () => {\n    class BoomLLM extends LLM {\n      label(): string {\n        return 'boom-llm';\n      }\n\n      chat(_: { chatCtx: ChatContext }): LLMStream {\n        throw new Error('boom');\n      }\n    }\n\n    const d = detector({ options: { llm: new BoomLLM() } });\n    // a failed pass is logged and re-raised on the (fire-and-forget) task; state is untouched\n    await expect(d.runOnce(ctx())).rejects.toThrow('boom');\n    expect(d.keyterms).toEqual([]);\n  });\n});\n\n// -- STT binding --\n\ndescribe('STT binding', () => {\n  it('push only on applied change', async () => {\n    const stt = new RecordingSTT();\n    const session = new FakeSession();\n    const d = detector({\n      staticKeyterms: ['Acme'],\n      options: {\n        enabled: true,\n        llm: new RecordingLLM([['Foo'], [], []], [[], ['Foo'], []]),\n      },\n    });\n    d.start(session, stt);\n    expect(stt.pushed).toEqual([['Acme']]); // start pushes the current set\n\n    session.addUser('u1');\n    await drain(d); // pending Foo: tracked, no applied change -> no push\n    expect(stt.pushed).toEqual([['Acme']]);\n\n    session.addUser('u2');\n    await drain(d); // confirm Foo: push\n    expect(stt.pushed[stt.pushed.length - 1]).toEqual(['Acme', 'Foo']);\n    await d.aclose();\n  });\n\n  it('detection llm metrics forwarded', async () => {\n    // the detector re-emits its detection LLM's usage, so it reaches the session metrics pipeline\n    const received: LLMMetrics[] = [];\n    const fake = new RecordingLLM();\n    const d = detector({ options: { enabled: true, llm: fake } });\n    d.on('metrics_collected', (m) => received.push(m));\n    d.start(new FakeSession(), new RecordingSTT());\n\n    const metrics: LLMMetrics = {\n      type: 'llm_metrics',\n      label: fake.label(),\n      requestId: 'r1',\n      timestamp: 0,\n      durationMs: 0,\n      ttftMs: 0,\n      cancelled: false,\n      completionTokens: 1,\n      promptTokens: 2,\n      promptCachedTokens: 0,\n      totalTokens: 3,\n      tokensPerSecond: 0,\n    };\n    fake.emit('metrics_collected', metrics);\n    expect(received).toEqual([metrics]);\n\n    await d.aclose(); // detaches from the detection LLM so a later emit is dropped\n    fake.emit('metrics_collected', metrics);\n    expect(received).toEqual([metrics]);\n  });\n\n  it('start same stt does not repush', async () => {\n    const stt = new RecordingSTT();\n    const session = new FakeSession();\n    const d = detector({\n      staticKeyterms: ['Acme'],\n      options: { enabled: true, llm: new RecordingLLM() },\n    });\n    d.start(session, stt);\n    expect(stt.pushed).toEqual([['Acme']]);\n    await d.aclose();\n    // re-binding the same instance on the next activity must not re-push (some STTs reconnect)\n    d.start(session, stt);\n    expect(stt.pushed).toEqual([['Acme']]);\n    await d.aclose();\n  });\n\n  it('static terms pushed without detection', async () => {\n    const stt = new RecordingSTT();\n    const session = new FakeSession();\n    const d = detector({ staticKeyterms: ['Acme'], options: { enabled: false } });\n    d.start(session, stt); // detection off must still bind the STT and push\n    expect(stt.pushed).toEqual([['Acme']]);\n    d.setStaticKeyterms(['New']);\n    expect(stt.pushed[stt.pushed.length - 1]).toEqual(['New']);\n    await d.aclose();\n  });\n\n  it('start pushes empty list to keyterm-capable stt', async () => {\n    const stt = new RecordingSTT();\n    const session = new FakeSession();\n    // simulate an earlier binding that left session keyterms on the instance\n    stt._updateSessionKeyterms(['Stale']);\n    const d = detector({ options: { enabled: false } });\n    d.start(session, stt); // empty set still pushed, so stale terms are cleared\n    expect(stt.pushed).toEqual([['Stale'], []]);\n    await d.aclose();\n  });\n\n  it('start without terms does not warn unsupported stt', async () => {\n    const stt = new RecordingSTT({ supportsKeyterms: false });\n    const session = new FakeSession();\n    const d = detector({ options: { enabled: false } });\n    d.start(session, stt); // nothing to apply + no capability -> no push, no warning\n    expect(stt.pushed).toEqual([]);\n    await d.aclose();\n  });\n\n  it('set static keyterms pushes', async () => {\n    const stt = new RecordingSTT();\n    const session = new FakeSession();\n    const d = detector({ options: { enabled: true, llm: new RecordingLLM() } });\n    d.start(session, stt);\n    d.setStaticKeyterms(['New']);\n    expect(stt.pushed[stt.pushed.length - 1]).toEqual(['New']);\n    await d.aclose();\n  });\n\n  it('unsupported stt warn and skip', () => {\n    const stt = new RecordingSTT({ supportsKeyterms: false });\n    // exercise the base method (warn-and-skip), not the recorder override\n    STT.prototype._updateSessionKeyterms.call(stt, ['a', 'b']);\n    expect(stt.pushed).toEqual([]);\n  });\n});\n\n// -- chat context sink (native carryover) --\n// forwarding (subscribe + push every turn) lives in AgentActivity; here we only cover the\n// STT sink contract: a supporting STT receives the pushed turns, an unsupported one warns.\n\ndescribe('chat context sink', () => {\n  it('push conversation item forwards to supporting stt', () => {\n    const stt = new RecordingSTT({ supportsChatContext: true });\n    const user = createConversationItemAddedEvent(\n      new ChatMessage({ role: 'user', content: ['hi'] }),\n    );\n    const agent = createConversationItemAddedEvent(\n      new ChatMessage({ role: 'assistant', content: ['Welcome'] }),\n    );\n    stt._pushConversationItem(user); // both user and agent turns are forwarded\n    stt._pushConversationItem(agent);\n    expect(stt.chatItems.map((m) => m.textContent)).toEqual(['hi', 'Welcome']);\n  });\n\n  it('unsupported stt chat ctx warn and skip', () => {\n    const stt = new RecordingSTT({ supportsChatContext: false });\n    const ev = createConversationItemAddedEvent(\n      new ChatMessage({ role: 'assistant', content: ['hi'] }),\n    );\n    // exercise the base method (warn-and-skip), not the recorder override\n    STT.prototype._pushConversationItem.call(stt, ev);\n    expect(stt.chatItems).toEqual([]);\n  });\n});\n\n// -- triggering --\n\ndescribe('triggering', () => {\n  it('triggers every n user turns', async () => {\n    const session = new FakeSession();\n    const fake = new RecordingLLM([[], ['Acme'], []]);\n    const d = detector({ options: { enabled: true, turnInterval: 2, llm: fake } });\n    d.start(session, new RecordingSTT());\n\n    session.addUser('first'); // below interval\n    await drain(d);\n    expect(fake.calls).toBe(0);\n\n    session.addAssistant('ack'); // assistant turns don't advance the counter\n    await drain(d);\n    expect(fake.calls).toBe(0);\n\n    session.addUser('second'); // triggers\n    await drain(d);\n    expect(fake.calls).toBe(1);\n\n    await d.aclose();\n  });\n\n  it('ignores assistant messages for counting', async () => {\n    const session = new FakeSession();\n    const fake = new RecordingLLM();\n    const d = detector({ options: { enabled: true, llm: fake } });\n    d.start(session, new RecordingSTT());\n\n    session.addAssistant('hello');\n    await drain(d);\n    expect(fake.calls).toBe(0);\n\n    session.addUser('hi');\n    await drain(d);\n    expect(fake.calls).toBe(1);\n\n    await d.aclose();\n  });\n\n  it('empty user turn does not trigger', async () => {\n    const session = new FakeSession();\n    const fake = new RecordingLLM();\n    const d = detector({ options: { enabled: true, llm: fake } });\n    d.start(session, new RecordingSTT());\n\n    session.addUser('');\n    await drain(d);\n    expect(fake.calls).toBe(0);\n\n    await d.aclose();\n  });\n\n  it('single flight skips overlapping pass', async () => {\n    const session = new FakeSession();\n    const fake = new BlockingLLM();\n    const d = detector({ options: { enabled: true, llm: fake } });\n    d.start(session, new RecordingSTT());\n\n    session.addUser('first');\n    await Promise.resolve();\n    expect(fake.calls).toBe(1);\n\n    session.addUser('second'); // a pass is still in flight -> skipped, not queued\n    await Promise.resolve();\n    expect(fake.calls).toBe(1);\n\n    fake.gate.resolve();\n    await drain(d);\n    expect(fake.calls).toBe(1);\n    await d.aclose();\n  });\n\n  it('aclose cancels an in-flight pass without applying its result', async () => {\n    const session = new FakeSession();\n    // if the pass were allowed to finish, it would confirm \"Acme\"\n    const fake = new BlockingLLM([[], ['Acme'], []]);\n    const stt = new RecordingSTT();\n    const d = detector({ options: { enabled: true, llm: fake } });\n    d.start(session, stt);\n    expect(stt.pushed).toEqual([[]]); // bind-time push of the (empty) current set\n\n    session.addUser('hi');\n    await Promise.resolve();\n    expect(fake.calls).toBe(1);\n\n    // the gate is never resolved by the test: aclose must unblock the pass itself\n    // (via the abort signal) instead of waiting out the detection timeout\n    await d.aclose();\n\n    expect(d.keyterms).toEqual([]); // cancelled pass must not touch state\n    expect(stt.pushed).toEqual([[]]); // ...nor push to the STT\n  });\n\n  it('aclose unsubscribes', async () => {\n    const session = new FakeSession();\n    const fake = new RecordingLLM();\n    const d = detector({ options: { enabled: true, llm: fake } });\n    d.start(session, new RecordingSTT());\n    await d.aclose();\n\n    session.addUser('hi');\n    await Promise.resolve();\n    expect(fake.calls).toBe(0);\n  });\n\n  it('disabled detection does not trigger', async () => {\n    const session = new FakeSession();\n    const fake = new RecordingLLM([[], ['Acme'], []]);\n    const d = detector({ options: { enabled: false, llm: fake } });\n    d.start(session, new RecordingSTT());\n\n    session.addUser('the Acme Grand');\n    await drain(d);\n    expect(fake.calls).toBe(0);\n    expect(d.keyterms).toEqual([]);\n  });\n\n  it('unsupported stt skips detection', async () => {\n    // no point running LLM detection passes when the STT can't consume the keyterms\n    const session = new FakeSession();\n    const fake = new RecordingLLM([[], ['Acme'], []]);\n    const d = detector({ options: { enabled: true, llm: fake } });\n    d.start(session, new RecordingSTT({ supportsKeyterms: false }));\n\n    session.addUser('the Acme Grand');\n    await drain(d);\n    expect(fake.calls).toBe(0);\n  });\n});\n\n// -- module helpers --\n\ndescribe('module helpers', () => {\n  it('detect keyterms parses result', async () => {\n    const llm = new RecordingLLM([[], ['Niamh'], ['Jon']]);\n    const [pending, confirm, remove] = await detectKeyterms(llm, ctx(\"It's Niamh\"), {\n      currentKeyterms: [],\n    });\n    expect(pending).toEqual([]);\n    expect(confirm).toEqual(['Niamh']);\n    expect(remove).toEqual(['Jon']);\n    // no transcript -> no LLM call, empty result\n    expect(await detectKeyterms(llm, ChatContext.empty())).toEqual([[], [], []]);\n  });\n\n  it('parse tool call', () => {\n    const call = new FunctionCall({\n      callId: '1',\n      name: 'record_keyterms',\n      args: JSON.stringify({\n        pending: ['John', '  ', 5], // blanks and non-strings are dropped\n        confirm: ['Foo'],\n        remove: ['Jon'],\n      }),\n    });\n    const [pending, confirm, remove] = parseToolCall([call]);\n    expect(pending).toEqual(['John']);\n    expect(confirm).toEqual(['Foo']);\n    expect(remove).toEqual(['Jon']);\n  });\n\n  it('parse tool call missing', () => {\n    expect(parseToolCall([])).toEqual([[], [], []]);\n    const bad = new FunctionCall({ callId: '1', name: 'record_keyterms', args: 'not json' });\n    expect(parseToolCall([bad])).toEqual([[], [], []]);\n  });\n\n  it('format input splits applied and candidate', () => {\n    const text = formatInput(ctx('hi'), [\n      ['Term1', true],\n      ['Term2', false],\n    ]);\n    expect(text).toBeDefined();\n    expect(text).toContain('Applied keyterms');\n    expect(text).toContain('Term1');\n    expect(text).toContain('Candidate keyterms');\n    expect(text).toContain('Term2');\n    expect(text).toContain('record_keyterms'); // trailing instruction\n    // no transcript yet -> nothing to send\n    expect(formatInput(ChatContext.empty(), [])).toBeUndefined();\n  });\n\n  it('resolve detection', () => {\n    expect(resolveDetection(undefined).enabled).toBe(false);\n    expect(resolveDetection({ enabled: false }).enabled).toBe(false);\n\n    const resolved = resolveDetection({ enabled: true });\n    expect(resolved.enabled).toBe(true);\n    expect(resolved.turnInterval).toBe(1);\n    expect(resolved.maxKeyterms).toBeUndefined();\n  });\n});\n"],"mappings":"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","names":[]}