@kuralle-syrinx/server-websocket 4.4.1 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/backchannel-cue-fixtures.d.ts +4 -0
  2. package/dist/backchannel-cue-fixtures.d.ts.map +1 -0
  3. package/dist/backchannel-cue-fixtures.js +25 -0
  4. package/dist/backchannel-cue-fixtures.js.map +1 -0
  5. package/dist/background-audio.d.ts +59 -0
  6. package/dist/background-audio.d.ts.map +1 -0
  7. package/dist/background-audio.js +248 -0
  8. package/dist/background-audio.js.map +1 -0
  9. package/dist/browser-opus.d.ts +12 -0
  10. package/dist/browser-opus.d.ts.map +1 -0
  11. package/dist/browser-opus.js +48 -0
  12. package/dist/browser-opus.js.map +1 -0
  13. package/dist/carrier-commands.d.ts +124 -0
  14. package/dist/carrier-commands.d.ts.map +1 -0
  15. package/dist/carrier-commands.js +193 -0
  16. package/dist/carrier-commands.js.map +1 -0
  17. package/dist/edge-telnyx.d.ts +37 -0
  18. package/dist/edge-telnyx.d.ts.map +1 -0
  19. package/dist/edge-telnyx.js +320 -0
  20. package/dist/edge-telnyx.js.map +1 -0
  21. package/dist/edge-twilio.d.ts +31 -0
  22. package/dist/edge-twilio.d.ts.map +1 -0
  23. package/dist/edge-twilio.js +286 -0
  24. package/dist/edge-twilio.js.map +1 -0
  25. package/dist/edge.d.ts +71 -0
  26. package/dist/edge.d.ts.map +1 -0
  27. package/dist/edge.js +560 -0
  28. package/dist/edge.js.map +1 -0
  29. package/dist/inbound-audio.d.ts +13 -0
  30. package/dist/inbound-audio.d.ts.map +1 -0
  31. package/dist/inbound-audio.js +66 -0
  32. package/dist/inbound-audio.js.map +1 -0
  33. package/dist/index.d.ts +81 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +718 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/json-message.d.ts +7 -0
  38. package/dist/json-message.d.ts.map +1 -0
  39. package/dist/json-message.js +41 -0
  40. package/dist/json-message.js.map +1 -0
  41. package/dist/outbound-playout-pipeline.d.ts +53 -0
  42. package/dist/outbound-playout-pipeline.d.ts.map +1 -0
  43. package/dist/outbound-playout-pipeline.js +148 -0
  44. package/dist/outbound-playout-pipeline.js.map +1 -0
  45. package/dist/paced-playout.d.ts +42 -0
  46. package/dist/paced-playout.d.ts.map +1 -0
  47. package/dist/paced-playout.js +151 -0
  48. package/dist/paced-playout.js.map +1 -0
  49. package/dist/playout-progress.d.ts +26 -0
  50. package/dist/playout-progress.d.ts.map +1 -0
  51. package/dist/playout-progress.js +64 -0
  52. package/dist/playout-progress.js.map +1 -0
  53. package/dist/session-store.d.ts +39 -0
  54. package/dist/session-store.d.ts.map +1 -0
  55. package/dist/session-store.js +80 -0
  56. package/dist/session-store.js.map +1 -0
  57. package/dist/smartpbx.d.ts +47 -0
  58. package/dist/smartpbx.d.ts.map +1 -0
  59. package/dist/smartpbx.js +437 -0
  60. package/dist/smartpbx.js.map +1 -0
  61. package/dist/telnyx-codec.d.ts +41 -0
  62. package/dist/telnyx-codec.d.ts.map +1 -0
  63. package/dist/telnyx-codec.js +114 -0
  64. package/dist/telnyx-codec.js.map +1 -0
  65. package/dist/telnyx.d.ts +52 -0
  66. package/dist/telnyx.d.ts.map +1 -0
  67. package/dist/telnyx.js +550 -0
  68. package/dist/telnyx.js.map +1 -0
  69. package/dist/test-helpers.d.ts +20 -0
  70. package/dist/test-helpers.d.ts.map +1 -0
  71. package/dist/test-helpers.js +242 -0
  72. package/dist/test-helpers.js.map +1 -0
  73. package/dist/transport-helpers.d.ts +12 -0
  74. package/dist/transport-helpers.d.ts.map +1 -0
  75. package/dist/transport-helpers.js +90 -0
  76. package/dist/transport-helpers.js.map +1 -0
  77. package/dist/transport-host.d.ts +56 -0
  78. package/dist/transport-host.d.ts.map +1 -0
  79. package/dist/transport-host.js +110 -0
  80. package/dist/transport-host.js.map +1 -0
  81. package/dist/turn-metrics.d.ts +79 -0
  82. package/dist/turn-metrics.d.ts.map +1 -0
  83. package/dist/turn-metrics.js +208 -0
  84. package/dist/turn-metrics.js.map +1 -0
  85. package/dist/twilio-auth.d.ts +12 -0
  86. package/dist/twilio-auth.d.ts.map +1 -0
  87. package/dist/twilio-auth.js +44 -0
  88. package/dist/twilio-auth.js.map +1 -0
  89. package/dist/twilio.d.ts +56 -0
  90. package/dist/twilio.d.ts.map +1 -0
  91. package/dist/twilio.js +511 -0
  92. package/dist/twilio.js.map +1 -0
  93. package/dist/websocket-close.d.ts +12 -0
  94. package/dist/websocket-close.d.ts.map +1 -0
  95. package/dist/websocket-close.js +58 -0
  96. package/dist/websocket-close.js.map +1 -0
  97. package/dist/websocket-lifecycle.d.ts +29 -0
  98. package/dist/websocket-lifecycle.d.ts.map +1 -0
  99. package/dist/websocket-lifecycle.js +87 -0
  100. package/dist/websocket-lifecycle.js.map +1 -0
  101. package/dist/websocket-upgrade.d.ts +9 -0
  102. package/dist/websocket-upgrade.d.ts.map +1 -0
  103. package/dist/websocket-upgrade.js +91 -0
  104. package/dist/websocket-upgrade.js.map +1 -0
  105. package/dist/wire-carrier-control.d.ts +27 -0
  106. package/dist/wire-carrier-control.d.ts.map +1 -0
  107. package/dist/wire-carrier-control.js +116 -0
  108. package/dist/wire-carrier-control.js.map +1 -0
  109. package/package.json +32 -11
  110. package/src/admission-control.test.ts +270 -0
  111. package/src/background-audio.test.ts +224 -0
  112. package/src/browser-opus.test.ts +41 -0
  113. package/src/browser-pacing.test.ts +440 -0
  114. package/src/carrier-commands.test.ts +198 -0
  115. package/src/edge-telnyx.test.ts +333 -0
  116. package/src/edge-twilio.test.ts +293 -0
  117. package/src/edge.test.ts +727 -0
  118. package/src/graceful-drain.test.ts +306 -0
  119. package/src/inbound-audio.test.ts +58 -0
  120. package/src/index.test.ts +2078 -0
  121. package/src/outbound-playout-pipeline.test.ts +314 -0
  122. package/src/paced-playout.test.ts +247 -0
  123. package/src/playout-progress.test.ts +56 -0
  124. package/src/session-store.test.ts +274 -0
  125. package/src/smartpbx.test.ts +986 -0
  126. package/src/telnyx.test.ts +1633 -0
  127. package/src/transport-host.test.ts +51 -0
  128. package/src/turn-metrics.test.ts +503 -0
  129. package/src/twilio-auth.test.ts +36 -0
  130. package/src/twilio.test.ts +1274 -0
  131. package/src/websocket-close.test.ts +63 -0
  132. package/src/websocket-lifecycle.test.ts +49 -0
@@ -0,0 +1,4 @@
1
+ import type { BackgroundAudioSource } from "./background-audio.js";
2
+ /** Placeholder cue map — pass as `backgroundAudio.cues` at session init. */
3
+ export declare function buildPlaceholderBackchannelCues(): Readonly<Record<string, BackgroundAudioSource>>;
4
+ //# sourceMappingURL=backchannel-cue-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backchannel-cue-fixtures.d.ts","sourceRoot":"","sources":["../src/backchannel-cue-fixtures.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAcnE,4EAA4E;AAC5E,wBAAgB,+BAA+B,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAMjG"}
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // Deterministic placeholder backchannel cue PCM (16 kHz mono). Real voice-matched
4
+ // assets + locale table are a follow-up — these shaped tones prove the byte-config path.
5
+ import { synthesizeTonePcm16 } from "@kuralle-syrinx/core";
6
+ import { pcm16BytesToSamples } from "@kuralle-syrinx/core/audio";
7
+ const CUE_SAMPLE_RATE_HZ = 16_000;
8
+ function placeholderCue(frequencyHz, durationMs, gain) {
9
+ const bytes = synthesizeTonePcm16({
10
+ frequencyHz,
11
+ durationMs,
12
+ sampleRateHz: CUE_SAMPLE_RATE_HZ,
13
+ amplitude: 0.35,
14
+ });
15
+ return { pcm: pcm16BytesToSamples(bytes), sampleRateHz: CUE_SAMPLE_RATE_HZ, gain };
16
+ }
17
+ /** Placeholder cue map — pass as `backgroundAudio.cues` at session init. */
18
+ export function buildPlaceholderBackchannelCues() {
19
+ return {
20
+ mm_hmm: placeholderCue(440, 400, 0.65),
21
+ yeah: placeholderCue(520, 350, 0.6),
22
+ got_it: placeholderCue(380, 450, 0.6),
23
+ };
24
+ }
25
+ //# sourceMappingURL=backchannel-cue-fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backchannel-cue-fixtures.js","sourceRoot":"","sources":["../src/backchannel-cue-fixtures.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,kFAAkF;AAClF,yFAAyF;AAEzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGjE,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,SAAS,cAAc,CAAC,WAAmB,EAAE,UAAkB,EAAE,IAAY;IAC3E,MAAM,KAAK,GAAG,mBAAmB,CAAC;QAChC,WAAW;QACX,UAAU;QACV,YAAY,EAAE,kBAAkB;QAChC,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IACH,OAAO,EAAE,GAAG,EAAE,mBAAmB,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;AACrF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,+BAA+B;IAC7C,OAAO;QACL,MAAM,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC;QACtC,IAAI,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;QACnC,MAAM,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;KACtC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { VoiceAgentSession } from "@kuralle-syrinx/core";
2
+ export interface BackgroundAudioSource {
3
+ /** Mono PCM16 samples. Must be loop-clean if it will loop audibly. */
4
+ readonly pcm: Int16Array;
5
+ readonly sampleRateHz: number;
6
+ /** Linear gain 0..1 applied to this source. */
7
+ readonly gain?: number;
8
+ }
9
+ export interface BackgroundAudioConfig {
10
+ /** Looped continuously: under speech (ducked) and alone between turns. */
11
+ readonly ambient?: BackgroundAudioSource;
12
+ /** Looped while `setThinking(true)` — the audible face of a pending tool call. */
13
+ readonly thinking?: BackgroundAudioSource;
14
+ /** Pre-cached one-shot backchannel cues keyed by cue id (IP-C3). */
15
+ readonly cues?: Readonly<Record<string, BackgroundAudioSource>>;
16
+ /**
17
+ * Extra multiplier on background sources while assistant speech is being
18
+ * mixed, so the bed never muddies the voice. 1 disables ducking. @default 0.5
19
+ */
20
+ readonly duckWhileSpeaking?: number;
21
+ /**
22
+ * Equal-power fade applied where the bed would otherwise hard-cut: the
23
+ * ambient's very first samples, and each thinking episode's start and stop.
24
+ * An abrupt full-volume onset is the harshest moment a caller hears from a
25
+ * wait — the fade is what makes an episode feel placed rather than switched.
26
+ * 0 disables. @default 250
27
+ */
28
+ readonly fadeMs?: number;
29
+ }
30
+ export declare class BackgroundAudioMixer {
31
+ private readonly ambient;
32
+ private readonly thinking;
33
+ private readonly cueCatalog;
34
+ private readonly duck;
35
+ private readonly fadeMs;
36
+ private thinkingState;
37
+ private thinkingFadeInPos;
38
+ private thinkingFadeOutPos;
39
+ private ambientFadeInPos;
40
+ private speakingUntilMs;
41
+ private pendingCue;
42
+ constructor(config: BackgroundAudioConfig);
43
+ get hasSources(): boolean;
44
+ hasCue(cueId: string): boolean;
45
+ isSpeaking(nowMs?: number): boolean;
46
+ /** Queue a one-shot backchannel cue by id. Returns false when the cue is unknown. */
47
+ queueCue(cueId: string): boolean;
48
+ /** G3 cue wiring: started/delayed → true, complete/failed → false. */
49
+ setThinking(on: boolean): void;
50
+ private thinkingScale;
51
+ private addBed;
52
+ private addPendingCue;
53
+ mix(chunk: Uint8Array, sampleRateHz: number, nowMs?: number): Uint8Array;
54
+ idleFrame(frameMs: number, sampleRateHz: number, nowMs?: number): Uint8Array | null;
55
+ }
56
+ export declare function wireBackgroundThinking(session: VoiceAgentSession, mixer: BackgroundAudioMixer): void;
57
+ export declare function wireBackgroundBackchannel(session: VoiceAgentSession, mixer: BackgroundAudioMixer): void;
58
+ export declare function wireBackgroundAudio(session: VoiceAgentSession, mixer: BackgroundAudioMixer): void;
59
+ //# sourceMappingURL=background-audio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"background-audio.d.ts","sourceRoot":"","sources":["../src/background-audio.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAgC,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI5F,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IACzC,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IAC1C,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAChE;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AA2HD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6C;IACxE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,UAAU,CAA8B;gBAEpC,MAAM,EAAE,qBAAqB;IAQzC,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI9B,UAAU,CAAC,KAAK,SAAa,GAAG,OAAO;IAIvC,qFAAqF;IACrF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAOhC,sEAAsE;IACtE,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI;IAa9B,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,MAAM;IAoCd,OAAO,CAAC,aAAa;IAMrB,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,SAAa,GAAG,UAAU;IAY5E,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,SAAa,GAAG,UAAU,GAAG,IAAI;CAWxF;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAIpG;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAYvG;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAGjG"}
@@ -0,0 +1,248 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // Background audio for the outbound path: a looped ambient bed (which doubles
4
+ // as telephony comfort noise between turns), an optional "thinking" loop keyed
5
+ // off the G3 tool-call cues, one-shot backchannel cues, and ducking under speech.
6
+ //
7
+ // Syrinx wires (Twilio media stream, browser jitter buffer) are single ORDERED
8
+ // streams — unlike LiveKit's WebRTC rooms there is no client-side track mixing
9
+ // — so background audio is mixed server-side into the one stream: `mix()` layers
10
+ // the bed under every TTS chunk (ducked), and `idleFrame()` produces bed-only
11
+ // frames for the gaps between turns. Source playback positions are shared
12
+ // between the two so the bed is seamless across turn boundaries.
13
+ //
14
+ // Runtime-neutral: no Node imports — runs on workerd (edge hosts) as-is.
15
+ // Prior art: Pipecat SoundfileMixer (transport-level mix + volume + loop),
16
+ // LiveKit BackgroundAudioPlayer (ambient/thinking split, state-keyed thinking).
17
+ import { Route } from "@kuralle-syrinx/core";
18
+ import { pcm16BytesToSamples, pcm16SamplesToBytes, resamplePcm16 } from "@kuralle-syrinx/core/audio";
19
+ const DEFAULT_AMBIENT_GAIN = 0.25;
20
+ const DEFAULT_THINKING_GAIN = 0.4;
21
+ const DEFAULT_CUE_GAIN = 0.65;
22
+ const DEFAULT_DUCK = 0.5;
23
+ const DEFAULT_FADE_MS = 250;
24
+ const CUE_THINKING_DUCK = 0.25;
25
+ function loopedSource(source, defaultGain) {
26
+ return {
27
+ original: source,
28
+ gain: source.gain ?? defaultGain,
29
+ byRate: new Map(),
30
+ position: 0,
31
+ positionRateHz: source.sampleRateHz,
32
+ };
33
+ }
34
+ function oneShotSource(source, defaultGain) {
35
+ return {
36
+ original: source,
37
+ gain: source.gain ?? defaultGain,
38
+ byRate: new Map(),
39
+ position: 0,
40
+ positionRateHz: source.sampleRateHz,
41
+ };
42
+ }
43
+ function samplesAtRate(source, rateHz) {
44
+ let samples = source.byRate.get(rateHz);
45
+ if (!samples) {
46
+ samples = resamplePcm16(source.original.pcm, source.original.sampleRateHz, rateHz);
47
+ source.byRate.set(rateHz, samples);
48
+ }
49
+ if (source.positionRateHz !== rateHz) {
50
+ source.position = Math.round((source.position * rateHz) / source.positionRateHz);
51
+ source.positionRateHz = rateHz;
52
+ }
53
+ return samples;
54
+ }
55
+ /**
56
+ * Add samples of `source` (scaled by `scale`, optionally shaped per-sample by
57
+ * `envelope(i)`) into `target`, advancing the loop.
58
+ */
59
+ function addLooped(target, source, rateHz, scale, envelope) {
60
+ const samples = samplesAtRate(source, rateHz);
61
+ if (samples.length === 0 || scale === 0)
62
+ return;
63
+ let pos = source.position % samples.length;
64
+ for (let i = 0; i < target.length; i += 1) {
65
+ const gain = envelope ? scale * envelope(i) : scale;
66
+ target[i] += samples[pos] * gain;
67
+ pos += 1;
68
+ if (pos >= samples.length)
69
+ pos = 0;
70
+ }
71
+ source.position = pos;
72
+ }
73
+ function addOneShot(target, source, rateHz) {
74
+ const samples = samplesAtRate(source, rateHz);
75
+ if (samples.length === 0 || source.gain === 0)
76
+ return true;
77
+ let pos = source.position;
78
+ for (let i = 0; i < target.length; i += 1) {
79
+ if (pos >= samples.length) {
80
+ source.position = samples.length;
81
+ return true;
82
+ }
83
+ target[i] += samples[pos] * source.gain;
84
+ pos += 1;
85
+ }
86
+ source.position = pos;
87
+ return pos >= samples.length;
88
+ }
89
+ /** Equal-power fade-in gain for sample `n` of a `total`-sample ramp (1 past the ramp). */
90
+ function fadeInGain(n, total) {
91
+ if (total <= 0 || n >= total)
92
+ return 1;
93
+ return Math.sin((n / total) * (Math.PI / 2));
94
+ }
95
+ /** Equal-power fade-out gain for sample `n` of a `total`-sample ramp (0 past the ramp). */
96
+ function fadeOutGain(n, total) {
97
+ if (total <= 0 || n >= total)
98
+ return 0;
99
+ return Math.cos((n / total) * (Math.PI / 2));
100
+ }
101
+ function clipToPcm16Bytes(mix) {
102
+ const out = new Int16Array(mix.length);
103
+ for (let i = 0; i < mix.length; i += 1) {
104
+ const v = Math.round(mix[i]);
105
+ out[i] = v > 32767 ? 32767 : v < -32768 ? -32768 : v;
106
+ }
107
+ return pcm16SamplesToBytes(out);
108
+ }
109
+ export class BackgroundAudioMixer {
110
+ ambient;
111
+ thinking;
112
+ cueCatalog;
113
+ duck;
114
+ fadeMs;
115
+ thinkingState = "off";
116
+ thinkingFadeInPos = 0;
117
+ thinkingFadeOutPos = 0;
118
+ ambientFadeInPos = 0;
119
+ speakingUntilMs = 0;
120
+ pendingCue = null;
121
+ constructor(config) {
122
+ this.ambient = config.ambient ? loopedSource(config.ambient, DEFAULT_AMBIENT_GAIN) : null;
123
+ this.thinking = config.thinking ? loopedSource(config.thinking, DEFAULT_THINKING_GAIN) : null;
124
+ this.cueCatalog = new Map(Object.entries(config.cues ?? {}));
125
+ this.duck = config.duckWhileSpeaking ?? DEFAULT_DUCK;
126
+ this.fadeMs = config.fadeMs ?? DEFAULT_FADE_MS;
127
+ }
128
+ get hasSources() {
129
+ return this.ambient !== null || this.thinking !== null || this.cueCatalog.size > 0;
130
+ }
131
+ hasCue(cueId) {
132
+ return this.cueCatalog.has(cueId);
133
+ }
134
+ isSpeaking(nowMs = Date.now()) {
135
+ return nowMs < this.speakingUntilMs;
136
+ }
137
+ /** Queue a one-shot backchannel cue by id. Returns false when the cue is unknown. */
138
+ queueCue(cueId) {
139
+ const source = this.cueCatalog.get(cueId);
140
+ if (!source)
141
+ return false;
142
+ this.pendingCue = oneShotSource(source, DEFAULT_CUE_GAIN);
143
+ return true;
144
+ }
145
+ /** G3 cue wiring: started/delayed → true, complete/failed → false. */
146
+ setThinking(on) {
147
+ if (on) {
148
+ if (this.thinkingState === "on")
149
+ return;
150
+ this.thinkingState = "on";
151
+ this.thinkingFadeInPos = 0;
152
+ if (this.thinking)
153
+ this.thinking.position = 0;
154
+ return;
155
+ }
156
+ if (this.thinkingState !== "on")
157
+ return;
158
+ this.thinkingState = this.fadeMs > 0 ? "stopping" : "off";
159
+ this.thinkingFadeOutPos = 0;
160
+ }
161
+ thinkingScale(bedScale) {
162
+ return this.pendingCue ? bedScale * CUE_THINKING_DUCK : bedScale;
163
+ }
164
+ addBed(mixBuf, sampleRateHz, bedScale) {
165
+ const fadeSamples = Math.round((this.fadeMs / 1000) * sampleRateHz);
166
+ const thinkingScale = this.thinkingScale(bedScale);
167
+ if (this.ambient) {
168
+ const startPos = this.ambientFadeInPos;
169
+ const envelope = startPos >= fadeSamples
170
+ ? undefined
171
+ : (i) => fadeInGain(startPos + i, fadeSamples);
172
+ addLooped(mixBuf, this.ambient, sampleRateHz, this.ambient.gain * bedScale, envelope);
173
+ this.ambientFadeInPos = Math.min(fadeSamples, startPos + mixBuf.length);
174
+ }
175
+ if (this.thinking && this.thinkingState !== "off") {
176
+ if (this.thinkingState === "on") {
177
+ const startPos = this.thinkingFadeInPos;
178
+ const envelope = startPos >= fadeSamples
179
+ ? undefined
180
+ : (i) => fadeInGain(startPos + i, fadeSamples);
181
+ addLooped(mixBuf, this.thinking, sampleRateHz, this.thinking.gain * thinkingScale, envelope);
182
+ this.thinkingFadeInPos = Math.min(fadeSamples, startPos + mixBuf.length);
183
+ }
184
+ else {
185
+ const startPos = this.thinkingFadeOutPos;
186
+ addLooped(mixBuf, this.thinking, sampleRateHz, this.thinking.gain * thinkingScale, (i) => fadeOutGain(startPos + i, fadeSamples));
187
+ this.thinkingFadeOutPos = startPos + mixBuf.length;
188
+ if (this.thinkingFadeOutPos >= fadeSamples)
189
+ this.thinkingState = "off";
190
+ }
191
+ }
192
+ }
193
+ addPendingCue(mixBuf, sampleRateHz) {
194
+ if (!this.pendingCue)
195
+ return;
196
+ const finished = addOneShot(mixBuf, this.pendingCue, sampleRateHz);
197
+ if (finished)
198
+ this.pendingCue = null;
199
+ }
200
+ mix(chunk, sampleRateHz, nowMs = Date.now()) {
201
+ const speech = pcm16BytesToSamples(chunk);
202
+ const durationMs = (speech.length / sampleRateHz) * 1000;
203
+ this.speakingUntilMs = Math.max(this.speakingUntilMs, nowMs) + durationMs;
204
+ if (!this.hasSources && !this.pendingCue)
205
+ return chunk;
206
+ const mixBuf = new Float64Array(speech.length);
207
+ for (let i = 0; i < speech.length; i += 1)
208
+ mixBuf[i] = speech[i];
209
+ this.addBed(mixBuf, sampleRateHz, this.duck);
210
+ return clipToPcm16Bytes(mixBuf);
211
+ }
212
+ idleFrame(frameMs, sampleRateHz, nowMs = Date.now()) {
213
+ if (this.isSpeaking(nowMs))
214
+ return null;
215
+ const playThinking = this.thinking !== null && this.thinkingState !== "off";
216
+ if (!this.ambient && !playThinking && !this.pendingCue)
217
+ return null;
218
+ const sampleCount = Math.max(1, Math.round((frameMs / 1000) * sampleRateHz));
219
+ const mixBuf = new Float64Array(sampleCount);
220
+ this.addBed(mixBuf, sampleRateHz, 1);
221
+ this.addPendingCue(mixBuf, sampleRateHz);
222
+ return clipToPcm16Bytes(mixBuf);
223
+ }
224
+ }
225
+ export function wireBackgroundThinking(session, mixer) {
226
+ session.on("tool_call_cue", (event) => {
227
+ mixer.setThinking(event.phase === "started" || event.phase === "delayed");
228
+ });
229
+ }
230
+ export function wireBackgroundBackchannel(session, mixer) {
231
+ session.bus.on("interaction.backchannel", (pkt) => {
232
+ const backchannel = pkt;
233
+ if (mixer.queueCue(backchannel.cue))
234
+ return;
235
+ session.bus.push(Route.Background, {
236
+ kind: "metric.conversation",
237
+ contextId: backchannel.contextId,
238
+ timestampMs: backchannel.timestampMs,
239
+ name: "backchannel.suppressed_missing_asset",
240
+ value: backchannel.cue,
241
+ });
242
+ });
243
+ }
244
+ export function wireBackgroundAudio(session, mixer) {
245
+ wireBackgroundThinking(session, mixer);
246
+ wireBackgroundBackchannel(session, mixer);
247
+ }
248
+ //# sourceMappingURL=background-audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"background-audio.js","sourceRoot":"","sources":["../src/background-audio.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const BROWSER_OPUS_SAMPLE_RATE_HZ = 48000;
2
+ export declare const BROWSER_OPUS_FRAME_DURATION_MS = 20;
3
+ export type BrowserOpusCodec = {
4
+ readonly sampleRateHz: number;
5
+ readonly frameSamples: number;
6
+ encodePcm16Frame(samples: Int16Array, flush?: boolean): Uint8Array[];
7
+ decodeOpusFrame(wire: Uint8Array): Int16Array;
8
+ reset(): void;
9
+ };
10
+ export declare function createBrowserOpusCodec(sampleRateHz?: number): BrowserOpusCodec;
11
+ export declare function decodeBrowserOpusToPcm16Bytes(wire: Uint8Array, codec: BrowserOpusCodec): Uint8Array;
12
+ //# sourceMappingURL=browser-opus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-opus.d.ts","sourceRoot":"","sources":["../src/browser-opus.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,CAAC;IACrE,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC9C,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,YAAY,SAA8B,GAAG,gBAAgB,CAsCnG;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,gBAAgB,GAAG,UAAU,CAEnG"}
@@ -0,0 +1,48 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import { Decoder as OpusDecoder, Encoder as OpusEncoder } from "@evan/opus";
3
+ import { pcm16BytesToSamples, pcm16SamplesToBytes } from "@kuralle-syrinx/core/audio";
4
+ export const BROWSER_OPUS_SAMPLE_RATE_HZ = 48_000;
5
+ export const BROWSER_OPUS_FRAME_DURATION_MS = 20;
6
+ export function createBrowserOpusCodec(sampleRateHz = BROWSER_OPUS_SAMPLE_RATE_HZ) {
7
+ const encoder = new OpusEncoder({ channels: 1, sample_rate: sampleRateHz, application: "voip" });
8
+ const decoder = new OpusDecoder({ channels: 1, sample_rate: sampleRateHz });
9
+ const frameSamples = Math.max(1, Math.round((sampleRateHz * BROWSER_OPUS_FRAME_DURATION_MS) / 1000));
10
+ let encodeRemainder = new Int16Array(0);
11
+ return {
12
+ sampleRateHz,
13
+ frameSamples,
14
+ reset() {
15
+ encodeRemainder = new Int16Array(0);
16
+ },
17
+ decodeOpusFrame(wire) {
18
+ return pcm16BytesToSamples(decoder.decode(wire));
19
+ },
20
+ encodePcm16Frame(samples, flush = false) {
21
+ const pending = new Int16Array(encodeRemainder.length + samples.length);
22
+ pending.set(encodeRemainder);
23
+ pending.set(samples, encodeRemainder.length);
24
+ const completeFrames = Math.floor(pending.length / frameSamples);
25
+ const frames = [];
26
+ for (let index = 0; index < completeFrames; index += 1) {
27
+ const frame = pending.subarray(index * frameSamples, (index + 1) * frameSamples);
28
+ frames.push(encoder.encode(pcm16SamplesToBytes(frame)));
29
+ }
30
+ const consumed = completeFrames * frameSamples;
31
+ const remainder = pending.subarray(consumed);
32
+ if (flush && remainder.length > 0) {
33
+ const padded = new Int16Array(frameSamples);
34
+ padded.set(remainder);
35
+ frames.push(encoder.encode(pcm16SamplesToBytes(padded)));
36
+ encodeRemainder = new Int16Array(0);
37
+ }
38
+ else {
39
+ encodeRemainder = new Int16Array(remainder);
40
+ }
41
+ return frames;
42
+ },
43
+ };
44
+ }
45
+ export function decodeBrowserOpusToPcm16Bytes(wire, codec) {
46
+ return pcm16SamplesToBytes(codec.decodeOpusFrame(wire));
47
+ }
48
+ //# sourceMappingURL=browser-opus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-opus.js","sourceRoot":"","sources":["../src/browser-opus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ import type { CallTransferPacket, DtmfSendPacket } from "@kuralle-syrinx/core";
2
+ export interface TwilioSendDigitsCommand {
3
+ readonly carrier: "twilio";
4
+ readonly kind: "send_digits";
5
+ /** REST Calls API path segment after /2010-04-01/Accounts/{AccountSid} */
6
+ readonly path: string;
7
+ readonly method: "POST";
8
+ readonly form: {
9
+ readonly SendDigits: string;
10
+ };
11
+ /** Equivalent TwiML fragment for a voice response webhook. */
12
+ readonly twiml: string;
13
+ }
14
+ export interface TelnyxSendDtmfCommand {
15
+ readonly carrier: "telnyx";
16
+ readonly kind: "send_dtmf";
17
+ readonly path: string;
18
+ readonly method: "POST";
19
+ readonly json: {
20
+ readonly digits: string;
21
+ readonly duration_millis?: number;
22
+ };
23
+ }
24
+ export type SendDtmfCommand = TwilioSendDigitsCommand | TelnyxSendDtmfCommand;
25
+ /**
26
+ * Twilio DTMF via Calls API `SendDigits` / TwiML `<Play digits="...">`.
27
+ * Pause letters `w`/`W` pass through unchanged (Twilio's native pause syntax).
28
+ * Live dispatch unverified.
29
+ */
30
+ export declare function buildTwilioSendDigits(callSid: string, packet: Pick<DtmfSendPacket, "digits">): TwilioSendDigitsCommand;
31
+ /**
32
+ * Telnyx Call Control `send_dtmf`. Pause letters retained in the digits string.
33
+ * Live dispatch unverified.
34
+ */
35
+ export declare function buildTelnyxSendDtmf(callControlId: string, packet: Pick<DtmfSendPacket, "digits">, opts?: {
36
+ readonly durationMillis?: number;
37
+ }): TelnyxSendDtmfCommand;
38
+ export interface TwilioTransferCommand {
39
+ readonly carrier: "twilio";
40
+ readonly kind: "transfer";
41
+ readonly mode: CallTransferPacket["mode"];
42
+ readonly path: string;
43
+ readonly method: "POST";
44
+ /**
45
+ * Calls API redirect: set Url to a TwiML Bin / webhook that dials the target.
46
+ * For cold/warm we emit the Dial TwiML the webhook would return.
47
+ */
48
+ readonly form: {
49
+ readonly Url?: string;
50
+ readonly Method?: "POST" | "GET";
51
+ readonly Twiml?: string;
52
+ };
53
+ readonly twiml: string;
54
+ /** Warm-handoff summary when mode is warm (not on the wire — for the receiving app). */
55
+ readonly summary?: string;
56
+ }
57
+ export interface TelnyxTransferCommand {
58
+ readonly carrier: "telnyx";
59
+ readonly kind: "transfer";
60
+ readonly mode: CallTransferPacket["mode"];
61
+ readonly path: string;
62
+ readonly method: "POST";
63
+ readonly json: {
64
+ readonly to: string;
65
+ /** Prefer Call-Control transfer over SIP REFER (attestation B penalty). */
66
+ readonly command_id?: string;
67
+ readonly client_state?: string;
68
+ readonly webhook_url?: string;
69
+ };
70
+ readonly summary?: string;
71
+ }
72
+ export type TransferCommand = TwilioTransferCommand | TelnyxTransferCommand;
73
+ /**
74
+ * Twilio transfer via Calls API Twiml/Url redirect.
75
+ * Prefer `<Dial>` (Call-Control style) over SIP REFER — REFER drops STIR/SHAKEN
76
+ * attestation to B and hurts answer rate.
77
+ * Live dispatch unverified.
78
+ */
79
+ export declare function buildTwilioTransfer(callSid: string, packet: Pick<CallTransferPacket, "mode" | "target" | "summary">, opts?: {
80
+ readonly redirectUrl?: string;
81
+ }): TwilioTransferCommand;
82
+ /**
83
+ * Telnyx Call Control `transfer`. Prefer this over SIP REFER (mode sip_refer still
84
+ * uses the transfer action with the SIP URI as `to` — Call Control, not raw REFER).
85
+ * Live dispatch unverified.
86
+ */
87
+ export declare function buildTelnyxTransfer(callControlId: string, packet: Pick<CallTransferPacket, "mode" | "target" | "summary">, opts?: {
88
+ readonly webhookUrl?: string;
89
+ readonly commandId?: string;
90
+ }): TelnyxTransferCommand;
91
+ export interface TwilioRestCredentials {
92
+ readonly accountSid: string;
93
+ readonly authToken: string;
94
+ /** Default https://api.twilio.com/2010-04-01/Accounts/{AccountSid} */
95
+ readonly baseUrl?: string;
96
+ }
97
+ export interface TelnyxRestCredentials {
98
+ readonly apiKey: string;
99
+ /** Default https://api.telnyx.com */
100
+ readonly baseUrl?: string;
101
+ }
102
+ export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
103
+ /**
104
+ * Dispatch a Twilio form POST via global `fetch()`. Credentials injected —
105
+ * never reads process.env (Workers-safe). Live call unverified.
106
+ */
107
+ export declare function dispatchTwilioCommand(creds: TwilioRestCredentials, command: TwilioSendDigitsCommand | TwilioTransferCommand, fetchImpl?: FetchLike): Promise<Response>;
108
+ /**
109
+ * Dispatch a Telnyx Call Control JSON POST via global `fetch()`. Credentials
110
+ * injected — never reads process.env (Workers-safe). Live call unverified.
111
+ */
112
+ export declare function dispatchTelnyxCommand(creds: TelnyxRestCredentials, command: TelnyxSendDtmfCommand | TelnyxTransferCommand, fetchImpl?: FetchLike): Promise<Response>;
113
+ /**
114
+ * Injectable summarizer for warm transfer. The transport calls this when a
115
+ * `call.transfer` packet has mode `"warm"` and no `summary` yet. Do NOT build
116
+ * a bespoke LLM here — inject your reasoner/app hook.
117
+ */
118
+ export type WarmTransferSummarizer = (args: {
119
+ readonly contextId: string;
120
+ readonly target: string;
121
+ }) => string | Promise<string | undefined> | undefined;
122
+ /** Resolve summary: packet.summary wins; else optional summarizer; else undefined. */
123
+ export declare function resolveTransferSummary(packet: CallTransferPacket, summarizer?: WarmTransferSummarizer): Promise<string | undefined>;
124
+ //# sourceMappingURL=carrier-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carrier-commands.d.ts","sourceRoot":"","sources":["../src/carrier-commands.ts"],"names":[],"mappings":"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"}