@kuralle-syrinx/core 4.4.0 → 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 (217) hide show
  1. package/dist/audio/alaw.d.ts +3 -0
  2. package/dist/audio/alaw.d.ts.map +1 -0
  3. package/dist/audio/alaw.js +53 -0
  4. package/dist/audio/alaw.js.map +1 -0
  5. package/dist/audio/g722.d.ts +39 -0
  6. package/dist/audio/g722.d.ts.map +1 -0
  7. package/dist/audio/g722.js +286 -0
  8. package/dist/audio/g722.js.map +1 -0
  9. package/dist/audio/index.d.ts +7 -0
  10. package/dist/audio/index.d.ts.map +1 -0
  11. package/dist/audio/index.js +8 -0
  12. package/dist/audio/index.js.map +1 -0
  13. package/dist/audio/loudness.d.ts +13 -0
  14. package/dist/audio/loudness.d.ts.map +1 -0
  15. package/dist/audio/loudness.js +65 -0
  16. package/dist/audio/loudness.js.map +1 -0
  17. package/dist/audio/mulaw.d.ts +3 -0
  18. package/dist/audio/mulaw.d.ts.map +1 -0
  19. package/dist/audio/mulaw.js +39 -0
  20. package/dist/audio/mulaw.js.map +1 -0
  21. package/dist/audio/pcm.d.ts +5 -0
  22. package/dist/audio/pcm.d.ts.map +1 -0
  23. package/dist/audio/pcm.js +40 -0
  24. package/dist/audio/pcm.js.map +1 -0
  25. package/dist/audio/resample.d.ts +12 -0
  26. package/dist/audio/resample.d.ts.map +1 -0
  27. package/dist/audio/resample.js +157 -0
  28. package/dist/audio/resample.js.map +1 -0
  29. package/dist/audio-envelope.d.ts +23 -0
  30. package/dist/audio-envelope.d.ts.map +1 -0
  31. package/dist/audio-envelope.js +114 -0
  32. package/dist/audio-envelope.js.map +1 -0
  33. package/dist/confidence-to-wait.d.ts +12 -0
  34. package/dist/confidence-to-wait.d.ts.map +1 -0
  35. package/dist/confidence-to-wait.js +18 -0
  36. package/dist/confidence-to-wait.js.map +1 -0
  37. package/dist/conversation-event.d.ts +19 -0
  38. package/dist/conversation-event.d.ts.map +1 -0
  39. package/dist/conversation-event.js +44 -0
  40. package/dist/conversation-event.js.map +1 -0
  41. package/dist/error-handler.d.ts +35 -0
  42. package/dist/error-handler.d.ts.map +1 -0
  43. package/dist/error-handler.js +136 -0
  44. package/dist/error-handler.js.map +1 -0
  45. package/dist/idle-timeout.d.ts +69 -0
  46. package/dist/idle-timeout.d.ts.map +1 -0
  47. package/dist/idle-timeout.js +149 -0
  48. package/dist/idle-timeout.js.map +1 -0
  49. package/dist/incremental-unit.d.ts +17 -0
  50. package/dist/incremental-unit.d.ts.map +1 -0
  51. package/dist/incremental-unit.js +5 -0
  52. package/dist/incremental-unit.js.map +1 -0
  53. package/dist/index.d.ts +47 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +61 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/init-chain.d.ts +35 -0
  58. package/dist/init-chain.d.ts.map +1 -0
  59. package/dist/init-chain.js +102 -0
  60. package/dist/init-chain.js.map +1 -0
  61. package/dist/init-stage-order.d.ts +5 -0
  62. package/dist/init-stage-order.d.ts.map +1 -0
  63. package/dist/init-stage-order.js +74 -0
  64. package/dist/init-stage-order.js.map +1 -0
  65. package/dist/interaction-coordinator.d.ts +45 -0
  66. package/dist/interaction-coordinator.d.ts.map +1 -0
  67. package/dist/interaction-coordinator.js +197 -0
  68. package/dist/interaction-coordinator.js.map +1 -0
  69. package/dist/interaction-policy.d.ts +103 -0
  70. package/dist/interaction-policy.d.ts.map +1 -0
  71. package/dist/interaction-policy.js +6 -0
  72. package/dist/interaction-policy.js.map +1 -0
  73. package/dist/iu-ledger.d.ts +39 -0
  74. package/dist/iu-ledger.d.ts.map +1 -0
  75. package/dist/iu-ledger.js +90 -0
  76. package/dist/iu-ledger.js.map +1 -0
  77. package/dist/latency-filler-fixtures.d.ts +7 -0
  78. package/dist/latency-filler-fixtures.d.ts.map +1 -0
  79. package/dist/latency-filler-fixtures.js +10 -0
  80. package/dist/latency-filler-fixtures.js.map +1 -0
  81. package/dist/latency-filler.d.ts +29 -0
  82. package/dist/latency-filler.d.ts.map +1 -0
  83. package/dist/latency-filler.js +102 -0
  84. package/dist/latency-filler.js.map +1 -0
  85. package/dist/mode-switcher.d.ts +24 -0
  86. package/dist/mode-switcher.d.ts.map +1 -0
  87. package/dist/mode-switcher.js +83 -0
  88. package/dist/mode-switcher.js.map +1 -0
  89. package/dist/observability-observer.d.ts +36 -0
  90. package/dist/observability-observer.d.ts.map +1 -0
  91. package/dist/observability-observer.js +141 -0
  92. package/dist/observability-observer.js.map +1 -0
  93. package/dist/observability.d.ts +68 -0
  94. package/dist/observability.d.ts.map +1 -0
  95. package/dist/observability.js +70 -0
  96. package/dist/observability.js.map +1 -0
  97. package/dist/packet-factories.d.ts +58 -0
  98. package/dist/packet-factories.d.ts.map +1 -0
  99. package/dist/packet-factories.js +181 -0
  100. package/dist/packet-factories.js.map +1 -0
  101. package/dist/packets.d.ts +532 -0
  102. package/dist/packets.d.ts.map +1 -0
  103. package/dist/packets.js +51 -0
  104. package/dist/packets.js.map +1 -0
  105. package/dist/pipeline-bus.d.ts +92 -0
  106. package/dist/pipeline-bus.d.ts.map +1 -0
  107. package/dist/pipeline-bus.js +294 -0
  108. package/dist/pipeline-bus.js.map +1 -0
  109. package/dist/plugin-contract.d.ts +73 -0
  110. package/dist/plugin-contract.d.ts.map +1 -0
  111. package/dist/plugin-contract.js +30 -0
  112. package/dist/plugin-contract.js.map +1 -0
  113. package/dist/policies/defer.d.ts +11 -0
  114. package/dist/policies/defer.d.ts.map +1 -0
  115. package/dist/policies/defer.js +13 -0
  116. package/dist/policies/defer.js.map +1 -0
  117. package/dist/policies/rule-based.d.ts +35 -0
  118. package/dist/policies/rule-based.d.ts.map +1 -0
  119. package/dist/policies/rule-based.js +130 -0
  120. package/dist/policies/rule-based.js.map +1 -0
  121. package/dist/pricing.d.ts +34 -0
  122. package/dist/pricing.d.ts.map +1 -0
  123. package/dist/pricing.js +108 -0
  124. package/dist/pricing.js.map +1 -0
  125. package/dist/primary-speaker-fixtures.d.ts +12 -0
  126. package/dist/primary-speaker-fixtures.d.ts.map +1 -0
  127. package/dist/primary-speaker-fixtures.js +37 -0
  128. package/dist/primary-speaker-fixtures.js.map +1 -0
  129. package/dist/primary-speaker-gate.d.ts +42 -0
  130. package/dist/primary-speaker-gate.d.ts.map +1 -0
  131. package/dist/primary-speaker-gate.js +178 -0
  132. package/dist/primary-speaker-gate.js.map +1 -0
  133. package/dist/provider-fallback.d.ts +28 -0
  134. package/dist/provider-fallback.d.ts.map +1 -0
  135. package/dist/provider-fallback.js +72 -0
  136. package/dist/provider-fallback.js.map +1 -0
  137. package/dist/reasoner-hedge.d.ts +21 -0
  138. package/dist/reasoner-hedge.d.ts.map +1 -0
  139. package/dist/reasoner-hedge.js +170 -0
  140. package/dist/reasoner-hedge.js.map +1 -0
  141. package/dist/reasoner-route.d.ts +24 -0
  142. package/dist/reasoner-route.d.ts.map +1 -0
  143. package/dist/reasoner-route.js +87 -0
  144. package/dist/reasoner-route.js.map +1 -0
  145. package/dist/reasoner-session-store.d.ts +18 -0
  146. package/dist/reasoner-session-store.d.ts.map +1 -0
  147. package/dist/reasoner-session-store.js +21 -0
  148. package/dist/reasoner-session-store.js.map +1 -0
  149. package/dist/reasoner.d.ts +97 -0
  150. package/dist/reasoner.d.ts.map +1 -0
  151. package/dist/reasoner.js +9 -0
  152. package/dist/reasoner.js.map +1 -0
  153. package/dist/retry.d.ts +31 -0
  154. package/dist/retry.d.ts.map +1 -0
  155. package/dist/retry.js +83 -0
  156. package/dist/retry.js.map +1 -0
  157. package/dist/scheduler.d.ts +11 -0
  158. package/dist/scheduler.d.ts.map +1 -0
  159. package/dist/scheduler.js +20 -0
  160. package/dist/scheduler.js.map +1 -0
  161. package/dist/spend-cap.d.ts +27 -0
  162. package/dist/spend-cap.d.ts.map +1 -0
  163. package/dist/spend-cap.js +38 -0
  164. package/dist/spend-cap.js.map +1 -0
  165. package/dist/tts-playout-clock.d.ts +43 -0
  166. package/dist/tts-playout-clock.d.ts.map +1 -0
  167. package/dist/tts-playout-clock.js +125 -0
  168. package/dist/tts-playout-clock.js.map +1 -0
  169. package/dist/turn-arbiter.d.ts +41 -0
  170. package/dist/turn-arbiter.d.ts.map +1 -0
  171. package/dist/turn-arbiter.js +259 -0
  172. package/dist/turn-arbiter.js.map +1 -0
  173. package/dist/voice-agent-session-util.d.ts +60 -0
  174. package/dist/voice-agent-session-util.d.ts.map +1 -0
  175. package/dist/voice-agent-session-util.js +204 -0
  176. package/dist/voice-agent-session-util.js.map +1 -0
  177. package/dist/voice-agent-session.d.ts +435 -0
  178. package/dist/voice-agent-session.d.ts.map +1 -0
  179. package/dist/voice-agent-session.js +1597 -0
  180. package/dist/voice-agent-session.js.map +1 -0
  181. package/dist/voice-text.d.ts +46 -0
  182. package/dist/voice-text.d.ts.map +1 -0
  183. package/dist/voice-text.js +209 -0
  184. package/dist/voice-text.js.map +1 -0
  185. package/package.json +19 -6
  186. package/src/audio/alaw.test.ts +87 -0
  187. package/src/audio/audio.test.ts +285 -0
  188. package/src/audio/g722.test.ts +149 -0
  189. package/src/audio/loudness.test.ts +58 -0
  190. package/src/audio-envelope.test.ts +167 -0
  191. package/src/confidence-to-wait.test.ts +21 -0
  192. package/src/error-handler.test.ts +56 -0
  193. package/src/hedge-throwing-backend.test.ts +46 -0
  194. package/src/interaction-coordinator.test.ts +529 -0
  195. package/src/iu-ledger.test.ts +276 -0
  196. package/src/latency-filler.test.ts +62 -0
  197. package/src/observability-observer.test.ts +248 -0
  198. package/src/observability.test.ts +94 -0
  199. package/src/packet-factories.test.ts +126 -0
  200. package/src/pipeline-bus.g10.test.ts +145 -0
  201. package/src/pipeline-bus.test.ts +211 -0
  202. package/src/policies/defer.test.ts +86 -0
  203. package/src/policies/rule-based.test.ts +334 -0
  204. package/src/pricing.test.ts +226 -0
  205. package/src/primary-speaker-gate.test.ts +150 -0
  206. package/src/provider-fallback.test.ts +87 -0
  207. package/src/reasoner-hedge.test.ts +361 -0
  208. package/src/reasoner-route.test.ts +248 -0
  209. package/src/reasoner.test.ts +69 -0
  210. package/src/retry.test.ts +83 -0
  211. package/src/route-throwing-spec.test.ts +44 -0
  212. package/src/spend-cap.test.ts +114 -0
  213. package/src/tts-playout-clock.test.ts +125 -0
  214. package/src/turn-arbiter.characterization.test.ts +477 -0
  215. package/src/turn-arbiter.test.ts +762 -0
  216. package/src/voice-agent-session.test.ts +4165 -0
  217. package/src/voice-text.test.ts +156 -0
package/dist/retry.js ADDED
@@ -0,0 +1,83 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export const DEFAULT_RETRY_CONFIG = {
3
+ maxAttempts: 3,
4
+ baseDelayMs: 250,
5
+ maxDelayMs: 2000,
6
+ };
7
+ /**
8
+ * Retry profile for long-lived voice-provider WebSockets (STT/TTS).
9
+ *
10
+ * Keeps the SAME fast first reconnect as the default (~125-250 ms) so a transient
11
+ * blip during a live call recovers without dead air, but raises the backoff cap to
12
+ * 10 s and allows more attempts so a persistent provider failure (rate limit /
13
+ * outage) is retried patiently instead of giving up after ~2 s. This intentionally
14
+ * does NOT use a multi-second *floor* (a 4 s first-retry would mean seconds of dead
15
+ * air on a live call); "every 10 ms matters" applies to the first reconnect, patient
16
+ * backoff applies only after repeated failures.
17
+ */
18
+ export const VOICE_PROVIDER_RETRY_CONFIG = {
19
+ maxAttempts: 6,
20
+ baseDelayMs: 250,
21
+ maxDelayMs: 10_000,
22
+ };
23
+ export const VOICE_PROVIDER_OUTAGE_RETRY_CONFIG = {
24
+ maxAttempts: 4,
25
+ baseDelayMs: 4_000,
26
+ maxDelayMs: 10_000,
27
+ };
28
+ export function readRetryConfig(config) {
29
+ return readRetryConfigWithDefaults(config, DEFAULT_RETRY_CONFIG);
30
+ }
31
+ /** Read retry config for a voice-provider WebSocket; defaults to the patient-backoff profile. */
32
+ export function readProviderRetryConfig(config) {
33
+ const profile = typeof config["retry_profile"] === "string" ? config["retry_profile"] : "";
34
+ return readRetryConfigWithDefaults(config, profile === "provider_outage" ? VOICE_PROVIDER_OUTAGE_RETRY_CONFIG : VOICE_PROVIDER_RETRY_CONFIG);
35
+ }
36
+ function readRetryConfigWithDefaults(config, defaults) {
37
+ return {
38
+ maxAttempts: readPositiveInteger(config["retry_max_attempts"], defaults.maxAttempts),
39
+ baseDelayMs: readPositiveInteger(config["retry_base_delay_ms"], defaults.baseDelayMs),
40
+ maxDelayMs: readPositiveInteger(config["retry_max_delay_ms"], defaults.maxDelayMs),
41
+ };
42
+ }
43
+ export function retryDelayMs(attemptIndex, config) {
44
+ const exponential = config.baseDelayMs * 2 ** Math.max(0, attemptIndex - 1);
45
+ return Math.min(config.maxDelayMs, exponential);
46
+ }
47
+ /**
48
+ * Equal-jitter backoff: half the deterministic exponential delay plus a random half.
49
+ * Spreads out retries so many sessions hitting the same provider rate/concurrency limit
50
+ * at once don't reconnect in lockstep (thundering herd). `rng` is injectable for tests.
51
+ */
52
+ export function retryDelayWithJitterMs(attemptIndex, config, rng = Math.random) {
53
+ const base = retryDelayMs(attemptIndex, config);
54
+ const half = base / 2;
55
+ return Math.round(half + rng() * half);
56
+ }
57
+ export async function waitForRetryDelay(attemptIndex, config, signal) {
58
+ const delayMs = retryDelayWithJitterMs(attemptIndex, config);
59
+ await new Promise((resolve, reject) => {
60
+ if (signal?.aborted) {
61
+ reject(abortError());
62
+ return;
63
+ }
64
+ const timeout = setTimeout(resolve, delayMs);
65
+ const onAbort = () => {
66
+ clearTimeout(timeout);
67
+ reject(abortError());
68
+ };
69
+ signal?.addEventListener("abort", onAbort, { once: true });
70
+ });
71
+ }
72
+ function abortError() {
73
+ const err = new Error("Retry aborted");
74
+ err.name = "AbortError";
75
+ return err;
76
+ }
77
+ function readPositiveInteger(value, fallback) {
78
+ if (typeof value !== "number" || !Number.isFinite(value))
79
+ return fallback;
80
+ const integer = Math.floor(value);
81
+ return integer > 0 ? integer : fallback;
82
+ }
83
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type ScheduledCallback = () => void | Promise<void>;
2
+ export interface Scheduler {
3
+ schedule(key: string, delayMs: number, cb: ScheduledCallback): void;
4
+ cancel(key: string): void;
5
+ }
6
+ export declare class TimerScheduler implements Scheduler {
7
+ private readonly timers;
8
+ schedule(key: string, delayMs: number, cb: ScheduledCallback): void;
9
+ cancel(key: string): void;
10
+ }
11
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoD;IAE3E,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI;IASnE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAM1B"}
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export class TimerScheduler {
3
+ timers = new Map();
4
+ schedule(key, delayMs, cb) {
5
+ this.cancel(key);
6
+ const timer = setTimeout(() => {
7
+ this.timers.delete(key);
8
+ void cb();
9
+ }, Math.max(0, delayMs));
10
+ this.timers.set(key, timer);
11
+ }
12
+ cancel(key) {
13
+ const timer = this.timers.get(key);
14
+ if (!timer)
15
+ return;
16
+ clearTimeout(timer);
17
+ this.timers.delete(key);
18
+ }
19
+ }
20
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAS/B,MAAM,OAAO,cAAc;IACR,MAAM,GAAG,IAAI,GAAG,EAAyC,CAAC;IAE3E,QAAQ,CAAC,GAAW,EAAE,OAAe,EAAE,EAAqB;QAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,KAAK,EAAE,EAAE,CAAC;QACZ,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import type { UsageRecordedPacket } from "./packets.js";
2
+ import { type PriceCatalog } from "./pricing.js";
3
+ export interface SpendCapConfig {
4
+ readonly maxUsd: number;
5
+ readonly catalog?: PriceCatalog;
6
+ }
7
+ export interface SpendCapCheck {
8
+ readonly exceeded: boolean;
9
+ readonly spentUsd: number;
10
+ }
11
+ /**
12
+ * Accumulates priced `usage.recorded` into session spend and reports whether a
13
+ * configured USD cap has been breached. Does not call providers; the session
14
+ * layer decides refuse/fallback from `check()`.
15
+ */
16
+ export declare class SpendCapGuard {
17
+ private readonly maxUsd;
18
+ private readonly catalog;
19
+ private spentUsd;
20
+ private exceeded;
21
+ constructor(cfg: SpendCapConfig);
22
+ /** Observe one usage packet. Unpriced usage does not change spentUsd. */
23
+ record(usage: UsageRecordedPacket): void;
24
+ /** Read-only policy view — never mutates spend. */
25
+ check(): SpendCapCheck;
26
+ }
27
+ //# sourceMappingURL=spend-cap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spend-cap.d.ts","sourceRoot":"","sources":["../src/spend-cap.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAiC,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEhF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAS;gBAEb,GAAG,EAAE,cAAc;IAQ/B,yEAAyE;IACzE,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IASxC,mDAAmD;IACnD,KAAK,IAAI,aAAa;CAGvB"}
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // Pure spend accumulator + policy. Observe (record) and control (check) are
4
+ // separated so the same usage packet is never double-counted by the guard.
5
+ import { costOf, DEFAULT_PRICE_CATALOG } from "./pricing.js";
6
+ /**
7
+ * Accumulates priced `usage.recorded` into session spend and reports whether a
8
+ * configured USD cap has been breached. Does not call providers; the session
9
+ * layer decides refuse/fallback from `check()`.
10
+ */
11
+ export class SpendCapGuard {
12
+ maxUsd;
13
+ catalog;
14
+ spentUsd = 0;
15
+ exceeded = false;
16
+ constructor(cfg) {
17
+ if (!Number.isFinite(cfg.maxUsd) || cfg.maxUsd < 0) {
18
+ throw new Error(`SpendCapGuard maxUsd must be a non-negative finite number, got ${String(cfg.maxUsd)}`);
19
+ }
20
+ this.maxUsd = cfg.maxUsd;
21
+ this.catalog = cfg.catalog ?? DEFAULT_PRICE_CATALOG;
22
+ }
23
+ /** Observe one usage packet. Unpriced usage does not change spentUsd. */
24
+ record(usage) {
25
+ const result = costOf(usage, this.catalog);
26
+ if (result.usd === null)
27
+ return;
28
+ this.spentUsd += result.usd;
29
+ if (this.spentUsd > this.maxUsd) {
30
+ this.exceeded = true;
31
+ }
32
+ }
33
+ /** Read-only policy view — never mutates spend. */
34
+ check() {
35
+ return { exceeded: this.exceeded, spentUsd: this.spentUsd };
36
+ }
37
+ }
38
+ //# sourceMappingURL=spend-cap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spend-cap.js","sourceRoot":"","sources":["../src/spend-cap.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAG3E,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAqB,MAAM,cAAc,CAAC;AAYhF;;;;GAIG;AACH,MAAM,OAAO,aAAa;IACP,MAAM,CAAS;IACf,OAAO,CAAe;IAC/B,QAAQ,GAAG,CAAC,CAAC;IACb,QAAQ,GAAG,KAAK,CAAC;IAEzB,YAAY,GAAmB;QAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,kEAAkE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC1G,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,qBAAqB,CAAC;IACtD,CAAC;IAED,yEAAyE;IACzE,MAAM,CAAC,KAA0B;QAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,MAAM,CAAC,GAAG,KAAK,IAAI;YAAE,OAAO;QAChC,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC;QAC5B,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,KAAK;QACH,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC9D,CAAC;CACF"}
@@ -0,0 +1,43 @@
1
+ import { type Scheduler } from "./scheduler.js";
2
+ export declare class TtsPlayoutClock {
3
+ private readonly scheduler;
4
+ private readonly active;
5
+ private readonly playoutEndMs;
6
+ private readonly releaseTimers;
7
+ private readonly realPlayoutContexts;
8
+ private readonly playedOutMsByContext;
9
+ constructor(scheduler?: Scheduler);
10
+ /**
11
+ * A TTS audio chunk arrived: mark the context active and advance its playout
12
+ * cursor by the chunk's realtime duration, anchored to `nowMs` if the previous
13
+ * estimate already lapsed (a gap in delivery).
14
+ */
15
+ noteAudio(contextId: string, audioDurationMs: number, nowMs: number): void;
16
+ /**
17
+ * Generation finished (tts.end). Keep the context interruptible until its
18
+ * playout estimate elapses, then release it. Releases immediately if the
19
+ * estimate has already passed.
20
+ */
21
+ scheduleRelease(contextId: string, nowMs: number): void;
22
+ /**
23
+ * Transport reported realtime playout progress for a context. Real playout is
24
+ * authoritative once seen; release the context when it reports complete.
25
+ */
26
+ noteProgress(contextId: string, complete: boolean, playedOutMs?: number): void;
27
+ positionMs(contextId: string): number | undefined;
28
+ release(contextId: string): void;
29
+ isActive(contextId: string): boolean;
30
+ /** All still-active contexts, insertion order (oldest first). */
31
+ activeContexts(): string[];
32
+ /**
33
+ * Wall-clock estimate of when this context's audio finishes playing out, or
34
+ * undefined if unknown. Anchors the idle timer to real playout end rather than
35
+ * chunk-arrival (TTS streams faster than realtime).
36
+ */
37
+ playoutEnd(contextId: string): number | undefined;
38
+ /** The most-recently-added still-active context (insertion order), or "" if none. */
39
+ latestActive(): string;
40
+ clear(): void;
41
+ private cancelRelease;
42
+ }
43
+ //# sourceMappingURL=tts-playout-clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tts-playout-clock.d.ts","sourceRoot":"","sources":["../src/tts-playout-clock.ts"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhE,qBAAa,eAAe;IAYd,OAAO,CAAC,QAAQ,CAAC,SAAS;IAXtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IAKnD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAA6B;gBAErC,SAAS,GAAE,SAAgC;IAExE;;;;OAIG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAO1E;;;;OAIG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAoBvD;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAM9E,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAQhC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIpC,iEAAiE;IACjE,cAAc,IAAI,MAAM,EAAE;IAI1B;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD,qFAAqF;IACrF,YAAY,IAAI,MAAM;IAMtB,KAAK,IAAI,IAAI;IAQb,OAAO,CAAC,aAAa;CAKtB"}
@@ -0,0 +1,125 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // Syrinx Kernel v2 — TTS Playout Clock
4
+ //
5
+ // Tracks when each context's streamed TTS audio finishes *playing out* (not just
6
+ // generating). TTS streams faster than realtime, so generation (tts.end) ends
7
+ // well before playout; turn-taking keys on these estimates so the assistant
8
+ // stays interruptible until it is actually done being heard.
9
+ //
10
+ // Pure state + timers — no bus, no other session coupling. The orchestrator owns
11
+ // wiring; this owns the playout bookkeeping.
12
+ import { TimerScheduler } from "./scheduler.js";
13
+ export class TtsPlayoutClock {
14
+ scheduler;
15
+ active = new Set();
16
+ // Wall-clock estimate of when each context's audio finishes playing out.
17
+ playoutEndMs = new Map();
18
+ releaseTimers = new Set();
19
+ // Contexts for which the output transport has reported real playout progress.
20
+ // When present, the transport's `complete` signal is authoritative and the
21
+ // sample-duration estimate defers to it (it is only a fallback for transports
22
+ // without a paced-playout layer, e.g. headless).
23
+ realPlayoutContexts = new Set();
24
+ playedOutMsByContext = new Map();
25
+ constructor(scheduler = new TimerScheduler()) {
26
+ this.scheduler = scheduler;
27
+ }
28
+ /**
29
+ * A TTS audio chunk arrived: mark the context active and advance its playout
30
+ * cursor by the chunk's realtime duration, anchored to `nowMs` if the previous
31
+ * estimate already lapsed (a gap in delivery).
32
+ */
33
+ noteAudio(contextId, audioDurationMs, nowMs) {
34
+ this.cancelRelease(contextId);
35
+ this.active.add(contextId);
36
+ const base = Math.max(nowMs, this.playoutEndMs.get(contextId) ?? nowMs);
37
+ this.playoutEndMs.set(contextId, base + audioDurationMs);
38
+ }
39
+ /**
40
+ * Generation finished (tts.end). Keep the context interruptible until its
41
+ * playout estimate elapses, then release it. Releases immediately if the
42
+ * estimate has already passed.
43
+ */
44
+ scheduleRelease(contextId, nowMs) {
45
+ const playoutEndMs = this.playoutEndMs.get(contextId);
46
+ const remainingMs = playoutEndMs === undefined ? 0 : playoutEndMs - nowMs;
47
+ if (remainingMs <= 0) {
48
+ this.release(contextId);
49
+ return;
50
+ }
51
+ this.cancelRelease(contextId);
52
+ this.releaseTimers.add(contextId);
53
+ this.scheduler.schedule(releaseKey(contextId), remainingMs, () => {
54
+ this.releaseTimers.delete(contextId);
55
+ // If a paced transport is reporting real playout for this context, it
56
+ // owns the release via noteProgress({complete}) — the estimate must not
57
+ // pre-empt it (real playout can run past the audio length under
58
+ // send-buffer backpressure). Session close() is the backstop.
59
+ if (this.realPlayoutContexts.has(contextId))
60
+ return;
61
+ this.release(contextId);
62
+ });
63
+ }
64
+ /**
65
+ * Transport reported realtime playout progress for a context. Real playout is
66
+ * authoritative once seen; release the context when it reports complete.
67
+ */
68
+ noteProgress(contextId, complete, playedOutMs) {
69
+ this.realPlayoutContexts.add(contextId);
70
+ if (playedOutMs !== undefined)
71
+ this.playedOutMsByContext.set(contextId, playedOutMs);
72
+ if (complete)
73
+ this.release(contextId);
74
+ }
75
+ positionMs(contextId) {
76
+ return this.playedOutMsByContext.get(contextId);
77
+ }
78
+ release(contextId) {
79
+ this.cancelRelease(contextId);
80
+ this.active.delete(contextId);
81
+ this.playoutEndMs.delete(contextId);
82
+ this.realPlayoutContexts.delete(contextId);
83
+ this.playedOutMsByContext.delete(contextId);
84
+ }
85
+ isActive(contextId) {
86
+ return this.active.has(contextId);
87
+ }
88
+ /** All still-active contexts, insertion order (oldest first). */
89
+ activeContexts() {
90
+ return [...this.active];
91
+ }
92
+ /**
93
+ * Wall-clock estimate of when this context's audio finishes playing out, or
94
+ * undefined if unknown. Anchors the idle timer to real playout end rather than
95
+ * chunk-arrival (TTS streams faster than realtime).
96
+ */
97
+ playoutEnd(contextId) {
98
+ return this.playoutEndMs.get(contextId);
99
+ }
100
+ /** The most-recently-added still-active context (insertion order), or "" if none. */
101
+ latestActive() {
102
+ let latest = "";
103
+ for (const contextId of this.active)
104
+ latest = contextId;
105
+ return latest;
106
+ }
107
+ clear() {
108
+ for (const contextId of [...this.releaseTimers])
109
+ this.cancelRelease(contextId);
110
+ this.active.clear();
111
+ this.playoutEndMs.clear();
112
+ this.realPlayoutContexts.clear();
113
+ this.playedOutMsByContext.clear();
114
+ }
115
+ cancelRelease(contextId) {
116
+ if (!this.releaseTimers.has(contextId))
117
+ return;
118
+ this.scheduler.cancel(releaseKey(contextId));
119
+ this.releaseTimers.delete(contextId);
120
+ }
121
+ }
122
+ function releaseKey(contextId) {
123
+ return `voice.tts_playout.release:${contextId}`;
124
+ }
125
+ //# sourceMappingURL=tts-playout-clock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tts-playout-clock.js","sourceRoot":"","sources":["../src/tts-playout-clock.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,uCAAuC;AACvC,EAAE;AACF,iFAAiF;AACjF,8EAA8E;AAC9E,4EAA4E;AAC5E,6DAA6D;AAC7D,EAAE;AACF,iFAAiF;AACjF,6CAA6C;AAE7C,OAAO,EAAE,cAAc,EAAkB,MAAM,gBAAgB,CAAC;AAEhE,MAAM,OAAO,eAAe;IAYG;IAXZ,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5C,yEAAyE;IACxD,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACnD,8EAA8E;IAC9E,2EAA2E;IAC3E,8EAA8E;IAC9E,iDAAiD;IAChC,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,oBAAoB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAElE,YAA6B,YAAuB,IAAI,cAAc,EAAE;QAA3C,cAAS,GAAT,SAAS,CAAkC;IAAG,CAAC;IAE5E;;;;OAIG;IACH,SAAS,CAAC,SAAiB,EAAE,eAAuB,EAAE,KAAa;QACjE,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC;QACxE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,GAAG,eAAe,CAAC,CAAC;IAC3D,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,SAAiB,EAAE,KAAa;QAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtD,MAAM,WAAW,GAAG,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1E,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAClC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YAC/D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACrC,sEAAsE;YACtE,wEAAwE;YACxE,gEAAgE;YAChE,8DAA8D;YAC9D,IAAI,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,OAAO;YACpD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,SAAiB,EAAE,QAAiB,EAAE,WAAoB;QACrE,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACrF,IAAI,QAAQ;YAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,CAAC,SAAiB;QACvB,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED,QAAQ,CAAC,SAAiB;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAED,iEAAiE;IACjE,cAAc;QACZ,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,qFAAqF;IACrF,YAAY;QACV,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,GAAG,SAAS,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK;QACH,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC;YAAE,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC/E,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,CAAC;IACpC,CAAC;IAEO,aAAa,CAAC,SAAiB;QACrC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO;QAC/C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;CACF;AAED,SAAS,UAAU,CAAC,SAAiB;IACnC,OAAO,6BAA6B,SAAS,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { type PipelineBus } from "./pipeline-bus.js";
2
+ import type { VadAudioPacket, VadSpeechActivityPacket, VadSpeechEndedPacket, VadSpeechStartedPacket } from "./packets.js";
3
+ import { PrimarySpeakerGate } from "./primary-speaker-gate.js";
4
+ import { TtsPlayoutClock } from "./tts-playout-clock.js";
5
+ export declare function isBackchannel(text: string): boolean;
6
+ export declare function isAcknowledgement(text: string, phrases: readonly string[]): boolean;
7
+ export interface TurnArbiterDeps {
8
+ readonly bus: PipelineBus;
9
+ readonly primarySpeakerGate: PrimarySpeakerGate;
10
+ readonly ttsPlayout: TtsPlayoutClock;
11
+ readonly minInterruptionMs: number;
12
+ readonly onInterrupt?: (interruptedContextId: string, source: "vad") => void;
13
+ /** When true, emit `interaction.duck` while evaluating a barge-in during active TTS,
14
+ * and `interaction.resume` if the pending window resolves without an interrupt. */
15
+ readonly pauseThenResolveBargeIn?: boolean;
16
+ }
17
+ export declare class TurnArbiter {
18
+ private readonly deps;
19
+ private turnInterruption;
20
+ private latestInterimText;
21
+ private latestInterimConfidence;
22
+ private ducked;
23
+ constructor(deps: TurnArbiterDeps);
24
+ noteInterimEvidence(text: string, confidence?: number): void;
25
+ onSpeechStarted(pkt: VadSpeechStartedPacket, interruptedContextId: string): void;
26
+ onSpeechActivity(pkt: VadSpeechActivityPacket): void;
27
+ onProviderSttEvidence(userContextId: string, timestampMs: number, interruptedContextId: string): void;
28
+ onSpeechEnded(pkt: VadSpeechEndedPacket, hasActiveTts: boolean): void;
29
+ observeBargeInAudio(pkt: VadAudioPacket): boolean;
30
+ emitInterruptDetected(interruptedContextId: string): void;
31
+ commitClientInterrupt(interruptedContextId: string): void;
32
+ clear(): void;
33
+ private pendingFor;
34
+ private transitionToPending;
35
+ private setAwaitingAudio;
36
+ private shouldDeferImmediateBargeInForSpeakerGate;
37
+ private tryCommit;
38
+ private decideInterrupt;
39
+ private suppress;
40
+ }
41
+ //# sourceMappingURL=turn-arbiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-arbiter.d.ts","sourceRoot":"","sources":["../src/turn-arbiter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAuCzD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAenF;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAChD,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;IAC7E;wFACoF;IACpF,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC5C;AAED,qBAAa,WAAW;IAMV,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,gBAAgB,CAA2C;IACnE,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,uBAAuB,CAAuB;IACtD,OAAO,CAAC,MAAM,CAAS;gBAEM,IAAI,EAAE,eAAe;IAElD,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAK5D,eAAe,CAAC,GAAG,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,GAAG,IAAI;IAqBhF,gBAAgB,CAAC,GAAG,EAAE,uBAAuB,GAAG,IAAI;IAYpD,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,IAAI;IAUrG,aAAa,CAAC,GAAG,EAAE,oBAAoB,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI;IAyBrE,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO;IAYjD,qBAAqB,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI;IAIzD,qBAAqB,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI;IAWzD,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,mBAAmB;IAwB3B,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,yCAAyC;IAKjD,OAAO,CAAC,SAAS;IA+CjB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,QAAQ;CAuBjB"}