@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
@@ -0,0 +1,156 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import { takeCompleteVoiceText, isCompleteVoiceText, appendVoiceText, normalizeForSpeech, stripLeakedToolCalls } from "./voice-text.js";
5
+
6
+ describe("stripLeakedToolCalls", () => {
7
+ it("removes XML-style tool_call blocks including inner JSON", () => {
8
+ expect(
9
+ stripLeakedToolCalls('Sure. <tool_call>{"name":"lookup","arguments":{"id":"7"}}</tool_call>'),
10
+ ).toBe("Sure.");
11
+ });
12
+
13
+ it("removes special sentinel tokens (Gemma/Llama/harmony) and Mistral markers", () => {
14
+ expect(stripLeakedToolCalls("<|tool_call|> checking now")).toBe("checking now");
15
+ expect(stripLeakedToolCalls("<|channel|>commentary answer")).toBe("answer");
16
+ expect(stripLeakedToolCalls("[TOOL_CALLS] one moment")).toBe("one moment");
17
+ });
18
+
19
+ it("leaves real speech with legitimate punctuation untouched", () => {
20
+ const prose = "The deadline is February 5th, and the fee is 40 dollars.";
21
+ expect(stripLeakedToolCalls(prose)).toBe(prose);
22
+ });
23
+
24
+ it("does NOT strip bare JSON (ambiguous — that's a serving-stack bug, not ours to guess)", () => {
25
+ const s = 'Your balance is {"amount": 42}.';
26
+ expect(stripLeakedToolCalls(s)).toBe(s);
27
+ });
28
+
29
+ it("is applied inside normalizeForSpeech so leaked markup never reaches TTS", () => {
30
+ expect(normalizeForSpeech("<tool_call>{}</tool_call>**Done.**")).toBe("Done.");
31
+ });
32
+ });
33
+
34
+ describe("normalizeForSpeech", () => {
35
+ it("strips bold, italic, strikethrough, and code markers", () => {
36
+ expect(normalizeForSpeech("This is **bold** and *italic* and `code`.")).toBe(
37
+ "This is bold and italic and code.",
38
+ );
39
+ expect(normalizeForSpeech("__strong__ and _em_ and ~~gone~~")).toBe("strong and em and gone");
40
+ });
41
+
42
+ it("reduces a markdown link to its label", () => {
43
+ expect(normalizeForSpeech("See [the deadline](https://x.edu/deadline) today.")).toBe(
44
+ "See the deadline today.",
45
+ );
46
+ });
47
+
48
+ it("removes leading block markers: headings, bullets, quotes, numbered lists", () => {
49
+ expect(normalizeForSpeech("## Deadlines")).toBe("Deadlines");
50
+ expect(normalizeForSpeech("- upload the form")).toBe("upload the form");
51
+ expect(normalizeForSpeech("1. first step")).toBe("first step");
52
+ expect(normalizeForSpeech("> a quote")).toBe("a quote");
53
+ });
54
+
55
+ it("leaves ordinary prose untouched", () => {
56
+ const prose = "Your late add deadline was February 5th, and the fee is 40 dollars.";
57
+ expect(normalizeForSpeech(prose)).toBe(prose);
58
+ });
59
+
60
+ it("does not mangle a lone asterisk or hash inside prose", () => {
61
+ // Conservative: an isolated * or # that isn't a paired/leading marker stays.
62
+ expect(normalizeForSpeech("Use the * key or press # to continue.")).toBe(
63
+ "Use the * key or press # to continue.",
64
+ );
65
+ });
66
+ });
67
+
68
+ describe("isCompleteVoiceText", () => {
69
+ it("treats terminal punctuation as complete", () => {
70
+ expect(isCompleteVoiceText("Hello there.")).toBe(true);
71
+ expect(isCompleteVoiceText("Really?!")).toBe(true);
72
+ });
73
+
74
+ it("treats an unterminated fragment as incomplete", () => {
75
+ expect(isCompleteVoiceText("Hello there")).toBe(false);
76
+ expect(isCompleteVoiceText("and then we")).toBe(false);
77
+ });
78
+
79
+ it("looks past trailing closing quotes/brackets to the terminator", () => {
80
+ expect(isCompleteVoiceText('She said "hi."')).toBe(true);
81
+ expect(isCompleteVoiceText("(a complete aside.)")).toBe(true);
82
+ expect(isCompleteVoiceText('an open quote "')).toBe(false);
83
+ });
84
+
85
+ it("recognizes non-English terminal punctuation", () => {
86
+ expect(isCompleteVoiceText("こんにちは。")).toBe(true); // Japanese full stop
87
+ expect(isCompleteVoiceText("مرحبا؟")).toBe(true); // Arabic question mark
88
+ expect(isCompleteVoiceText("नमस्ते।")).toBe(true); // Devanagari danda
89
+ });
90
+
91
+ it("does not treat abbreviation or decimal dots as sentence ends", () => {
92
+ // These would otherwise be voiced with a falling intonation and split from
93
+ // their continuation ("Dr." | "Smith", "twelve." | "fifty").
94
+ expect(isCompleteVoiceText("Dr.")).toBe(false);
95
+ expect(isCompleteVoiceText("e.g.")).toBe(false);
96
+ expect(isCompleteVoiceText("The total is $12.")).toBe(false);
97
+ expect(isCompleteVoiceText("Meet at 3 p.m.")).toBe(false);
98
+ expect(isCompleteVoiceText("His name is J.")).toBe(false);
99
+ // But a real sentence end after an abbreviation earlier in the text is fine.
100
+ expect(isCompleteVoiceText("Dr. Smith will see you now.")).toBe(true);
101
+ expect(isCompleteVoiceText("The total is $12.50 today.")).toBe(true);
102
+ });
103
+ });
104
+
105
+ describe("takeCompleteVoiceText", () => {
106
+ it("splits leading complete sentences from the incomplete remainder", () => {
107
+ const { text, remaining } = takeCompleteVoiceText("One. Two. Thre");
108
+ expect(text).toBe("One. Two.");
109
+ expect(remaining).toBe("Thre");
110
+ });
111
+
112
+ it("returns no text when nothing is complete yet", () => {
113
+ const { text, remaining } = takeCompleteVoiceText("still going");
114
+ expect(text).toBe("");
115
+ expect(remaining).toBe("still going");
116
+ });
117
+
118
+ it("emits multiple complete sentences and buffers only the trailing fragment", () => {
119
+ const { text, remaining } = takeCompleteVoiceText("Done. And more.");
120
+ expect(text).toBe("Done. And more.");
121
+ expect(remaining).toBe("");
122
+ });
123
+
124
+ it("buffers the trailing incomplete fragment after a complete sentence", () => {
125
+ // Capitalized continuation so the locale-aware segmenter treats it as a new
126
+ // (still-incomplete) sentence rather than one run-on.
127
+ const { text, remaining } = takeCompleteVoiceText("Done. And more");
128
+ expect(text).toBe("Done.");
129
+ expect(remaining).toBe("And more");
130
+ });
131
+ });
132
+
133
+ describe("appendVoiceText", () => {
134
+ it("seeds from empty and trims", () => {
135
+ expect(appendVoiceText("", " hi ")).toBe("hi");
136
+ });
137
+
138
+ it("joins with a single space when neither side has whitespace at the seam", () => {
139
+ expect(appendVoiceText("Hello", "there")).toBe("Hello there");
140
+ });
141
+
142
+ it("does not double-space when the existing side already ends in whitespace", () => {
143
+ expect(appendVoiceText("Hello ", "there")).toBe("Hello there");
144
+ });
145
+
146
+ it("trims a whitespace-led next fragment (the seam's space collapses)", () => {
147
+ // Current behavior: a leading-whitespace `next` is trimmed and concatenated
148
+ // directly. Reachable inputs (trimmed segment text) never hit this path; the
149
+ // test pins the documented behavior rather than the intuitive one.
150
+ expect(appendVoiceText("Hello", " there")).toBe("Hellothere");
151
+ });
152
+
153
+ it("returns the existing text unchanged when the next fragment is blank", () => {
154
+ expect(appendVoiceText("Hello", " ")).toBe("Hello");
155
+ });
156
+ });