@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,92 @@
1
+ import type { VoicePacket } from "./packets.js";
2
+ export declare enum Route {
3
+ Critical = 0,// interrupts, turn changes — drained first, never bounded
4
+ Main = 1,// pipeline flow: audio in, STT results, LLM deltas, TTS audio
5
+ Background = 2
6
+ }
7
+ export type PacketHandler<T extends VoicePacket = VoicePacket> = (pkt: T) => void | Promise<void>;
8
+ export interface PipelineBus {
9
+ /** Push one or more packets into a priority route. */
10
+ push<T extends readonly VoicePacket[]>(route: Route, ...packets: T): void;
11
+ /**
12
+ * Register a handler for a specific packet kind. Returns unsubscribe function.
13
+ *
14
+ * By default handlers are awaited in registration order (consumer semantics — the
15
+ * handler's state mutations are visible to the next packet's handlers). Pass
16
+ * `{ concurrent: true }` for a long-running PRODUCER handler (e.g. an LLM-generation
17
+ * loop that emits its own packets over time): it is dispatched fire-and-forget so it
18
+ * does not park the drain loop and defer subsequent Main packets / Critical interrupts
19
+ * behind it. Concurrent handler errors are surfaced as `pipeline.error`, like async packets.
20
+ */
21
+ on<T extends VoicePacket>(kind: T["kind"], handler: PacketHandler<T>, opts?: {
22
+ concurrent?: boolean;
23
+ }): () => void;
24
+ /** Start draining the bus. Resolves when stop() is called and final drain completes. */
25
+ start(): Promise<void>;
26
+ /** Stop draining. Flushes Critical+Main, discards Background. */
27
+ stop(): void;
28
+ /** Readonly stream of every packet pushed into the bus, before route dispatch. */
29
+ readonly allPackets: ReadableStream<{
30
+ route: Route;
31
+ packet: VoicePacket;
32
+ }>;
33
+ }
34
+ /**
35
+ * Configuration for PipelineBusImpl.
36
+ * Critical is always unbounded. Main and Background can be configured.
37
+ */
38
+ export interface PipelineBusConfig {
39
+ /** Maximum Main queue size. Default 4096. Throws on overflow. */
40
+ mainCapacity?: number;
41
+ /** Maximum Background queue size. Default 2048. Drops oldest on overflow. */
42
+ bgCapacity?: number;
43
+ /** Maximum Critical packets to batch per tick before yielding to I/O. Default 4. */
44
+ criticalBatchSize?: number;
45
+ /** Called when a Background packet is dropped. For metrics emission. */
46
+ onBackgroundDrop?: (dropped: VoicePacket) => void;
47
+ /** Called for every packet pushed into the bus. */
48
+ onPacket?: (route: Route, packet: VoicePacket) => void;
49
+ }
50
+ export declare class PipelineBusImpl implements PipelineBus {
51
+ private critical;
52
+ private main;
53
+ private background;
54
+ private handlers;
55
+ private concurrentHandlers;
56
+ private running;
57
+ private resolver;
58
+ private drainedCount;
59
+ private allPacketsController;
60
+ readonly allPackets: ReadableStream<{
61
+ route: Route;
62
+ packet: VoicePacket;
63
+ }>;
64
+ private readonly mainCapacity;
65
+ private readonly bgCapacity;
66
+ private readonly criticalBatchSize;
67
+ private readonly onBgDrop;
68
+ private readonly onPacket;
69
+ constructor(config?: PipelineBusConfig);
70
+ push<T extends readonly VoicePacket[]>(route: Route, ...packets: T): void;
71
+ on<T extends VoicePacket>(kind: T["kind"], handler: PacketHandler<T>, opts?: {
72
+ concurrent?: boolean;
73
+ }): () => void;
74
+ start(): Promise<void>;
75
+ stop(): void;
76
+ private queueFor;
77
+ private publishAllPackets;
78
+ private enqueueBackgroundDropMetric;
79
+ private capacityFor;
80
+ /**
81
+ * Dequeue a batch of packets. Always drains Critical first.
82
+ * Critical batches up to `criticalBatchSize` per tick before yielding to I/O.
83
+ * Main and Background drain one packet per tick.
84
+ */
85
+ private dequeueBatch;
86
+ /** Dispatch one packet to registered handlers. */
87
+ private dispatch;
88
+ private emitHandlerError;
89
+ /** Synchronous dispatch for drain-on-stop. Swallows errors. */
90
+ private dispatchSync;
91
+ }
92
+ //# sourceMappingURL=pipeline-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-bus.d.ts","sourceRoot":"","sources":["../src/pipeline-bus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,294 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // Syrinx Kernel v2 — Priority Pipeline Bus
4
+ //
5
+ // Three priority channels (Critical, Main, Background) with strict drain order.
6
+ // All pipeline components push packets into the bus. The bus dispatches to
7
+ // registered handlers. Handlers are registered by packet kind (discriminated string).
8
+ //
9
+ // Design decisions (per RFC Q1 resolution):
10
+ // - Uses setTimeout(fn, 0) for the drain loop to yield to I/O between iterations.
11
+ // - Critical channel batches up to 4 packets per tick before yielding.
12
+ // - Main queue is bounded at 4096 packets; throws on overflow.
13
+ // - Background queue is bounded at 2048 packets; drops oldest on overflow.
14
+ // - Pipeline handler errors emit VoiceErrorPacket on Critical route — bus continues.
15
+ import { ErrorCategory } from "./packets.js";
16
+ // =============================================================================
17
+ // Public Types
18
+ // =============================================================================
19
+ export var Route;
20
+ (function (Route) {
21
+ Route[Route["Critical"] = 0] = "Critical";
22
+ Route[Route["Main"] = 1] = "Main";
23
+ Route[Route["Background"] = 2] = "Background";
24
+ })(Route || (Route = {}));
25
+ // =============================================================================
26
+ // Implementation
27
+ // =============================================================================
28
+ export class PipelineBusImpl {
29
+ critical = [];
30
+ main = [];
31
+ background = [];
32
+ handlers = new Map();
33
+ concurrentHandlers = new Set();
34
+ running = false;
35
+ resolver = null;
36
+ drainedCount = 0;
37
+ allPacketsController = null;
38
+ allPackets;
39
+ mainCapacity;
40
+ bgCapacity;
41
+ criticalBatchSize;
42
+ onBgDrop;
43
+ onPacket;
44
+ constructor(config) {
45
+ this.mainCapacity = config?.mainCapacity ?? 4096;
46
+ this.bgCapacity = config?.bgCapacity ?? 2048;
47
+ this.criticalBatchSize = config?.criticalBatchSize ?? 4;
48
+ this.onBgDrop = config?.onBackgroundDrop;
49
+ this.onPacket = config?.onPacket;
50
+ this.allPackets = new ReadableStream({
51
+ start: (controller) => {
52
+ this.allPacketsController = controller;
53
+ },
54
+ cancel: () => {
55
+ this.allPacketsController = null;
56
+ },
57
+ });
58
+ if (this.mainCapacity < 1)
59
+ throw new Error("mainCapacity must be >= 1");
60
+ if (this.bgCapacity < 1)
61
+ throw new Error("bgCapacity must be >= 1");
62
+ if (this.criticalBatchSize < 1)
63
+ throw new Error("criticalBatchSize must be >= 1");
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Public API
67
+ // ---------------------------------------------------------------------------
68
+ push(route, ...packets) {
69
+ for (const p of packets) {
70
+ this.publishAllPackets(route, p);
71
+ const q = this.queueFor(route);
72
+ let droppedForMetric = null;
73
+ if (q.length >= this.capacityFor(route)) {
74
+ if (route === Route.Background) {
75
+ const dropped = q.shift();
76
+ if (dropped && this.onBgDrop) {
77
+ this.onBgDrop(dropped);
78
+ }
79
+ if (dropped) {
80
+ droppedForMetric = dropped;
81
+ }
82
+ // continue — push after dropping oldest
83
+ }
84
+ else if (route === Route.Main) {
85
+ throw new Error(`PipelineBus: Main queue full (${this.mainCapacity}). ` +
86
+ `Backpressure required — slow down producers or increase capacity.`);
87
+ }
88
+ // Critical is never bounded
89
+ }
90
+ q.push(p);
91
+ if (droppedForMetric) {
92
+ this.enqueueBackgroundDropMetric(droppedForMetric);
93
+ }
94
+ }
95
+ // Wake the drain loop
96
+ this.resolver?.();
97
+ this.resolver = null;
98
+ }
99
+ on(kind, handler, opts) {
100
+ let set = this.handlers.get(kind);
101
+ if (!set) {
102
+ set = new Set();
103
+ this.handlers.set(kind, set);
104
+ }
105
+ const h = handler;
106
+ set.add(h);
107
+ if (opts?.concurrent)
108
+ this.concurrentHandlers.add(h);
109
+ return () => {
110
+ set.delete(h);
111
+ this.concurrentHandlers.delete(h);
112
+ if (set.size === 0) {
113
+ this.handlers.delete(kind);
114
+ }
115
+ };
116
+ }
117
+ async start() {
118
+ this.running = true;
119
+ while (this.running) {
120
+ const batch = this.dequeueBatch();
121
+ if (batch.length === 0) {
122
+ // Yield to I/O — wait for next push()
123
+ await new Promise((resolve) => {
124
+ this.resolver = resolve;
125
+ });
126
+ continue;
127
+ }
128
+ for (const entry of batch) {
129
+ await this.dispatch(entry.packet);
130
+ this.drainedCount++;
131
+ }
132
+ }
133
+ }
134
+ stop() {
135
+ this.running = false;
136
+ // Drain remaining Critical then Main (synchronous — stop is a shutdown path)
137
+ while (this.critical.length > 0) {
138
+ const pkt = this.critical.shift();
139
+ void this.dispatchSync(pkt);
140
+ }
141
+ while (this.main.length > 0) {
142
+ const pkt = this.main.shift();
143
+ void this.dispatchSync(pkt);
144
+ }
145
+ // Discard Background
146
+ this.background.length = 0;
147
+ this.resolver?.();
148
+ this.resolver = null;
149
+ }
150
+ // ---------------------------------------------------------------------------
151
+ // Private
152
+ // ---------------------------------------------------------------------------
153
+ queueFor(r) {
154
+ if (r === Route.Critical)
155
+ return this.critical;
156
+ if (r === Route.Main)
157
+ return this.main;
158
+ return this.background;
159
+ }
160
+ publishAllPackets(route, packet) {
161
+ this.onPacket?.(route, packet);
162
+ if (!this.allPacketsController)
163
+ return;
164
+ // Only retain packets when a reader is actually attached. A ReadableStream
165
+ // buffers enqueue() without bound until read, so with no consumer (the default
166
+ // deployment — recorder is optional) this would retain every audio buffer of
167
+ // the whole call and OOM. `locked` is true once getReader() is called.
168
+ if (!this.allPackets.locked)
169
+ return;
170
+ try {
171
+ this.allPacketsController.enqueue({ route, packet });
172
+ }
173
+ catch {
174
+ this.allPacketsController = null;
175
+ }
176
+ }
177
+ enqueueBackgroundDropMetric(dropped) {
178
+ const metric = {
179
+ kind: "metric.conversation",
180
+ contextId: dropped.contextId,
181
+ timestampMs: Date.now(),
182
+ name: "pipeline.bus.background.dropped",
183
+ value: dropped.kind,
184
+ };
185
+ this.publishAllPackets(Route.Background, metric);
186
+ if (this.background.length >= this.bgCapacity) {
187
+ this.background.shift();
188
+ }
189
+ this.background.push(metric);
190
+ }
191
+ capacityFor(r) {
192
+ if (r === Route.Critical)
193
+ return Infinity;
194
+ if (r === Route.Main)
195
+ return this.mainCapacity;
196
+ return this.bgCapacity;
197
+ }
198
+ /**
199
+ * Dequeue a batch of packets. Always drains Critical first.
200
+ * Critical batches up to `criticalBatchSize` per tick before yielding to I/O.
201
+ * Main and Background drain one packet per tick.
202
+ */
203
+ dequeueBatch() {
204
+ // 1. Critical — batch up to N
205
+ if (this.critical.length > 0) {
206
+ const batch = [];
207
+ const count = Math.min(this.critical.length, this.criticalBatchSize);
208
+ for (let i = 0; i < count; i++) {
209
+ batch.push({
210
+ route: Route.Critical,
211
+ packet: this.critical.shift(),
212
+ });
213
+ }
214
+ return batch;
215
+ }
216
+ // 2. Main — one packet per tick
217
+ if (this.main.length > 0) {
218
+ return [{ route: Route.Main, packet: this.main.shift() }];
219
+ }
220
+ // 3. Background — one packet per tick
221
+ if (this.background.length > 0) {
222
+ return [{ route: Route.Background, packet: this.background.shift() }];
223
+ }
224
+ return [];
225
+ }
226
+ /** Dispatch one packet to registered handlers. */
227
+ async dispatch(pkt) {
228
+ const matches = this.handlers.get(pkt.kind);
229
+ if (!matches || matches.size === 0)
230
+ return;
231
+ // Async packets: fire-and-forget, don't await
232
+ if ("isAsync" in pkt && pkt.isAsync) {
233
+ for (const h of matches) {
234
+ void (async () => {
235
+ try {
236
+ await h(pkt);
237
+ }
238
+ catch {
239
+ // Fire-and-forget errors are intentionally swallowed.
240
+ // AsyncPackets are for non-critical telemetry/events.
241
+ }
242
+ })();
243
+ }
244
+ return;
245
+ }
246
+ // Sync packets: await each consumer handler in registration order. Concurrent
247
+ // (producer) handlers are fired fire-and-forget so a long-running handler does
248
+ // not park the drain loop and defer subsequent Main/Critical packets behind it.
249
+ for (const h of matches) {
250
+ const handler = h;
251
+ if (this.concurrentHandlers.has(handler)) {
252
+ void (async () => {
253
+ try {
254
+ await handler(pkt);
255
+ }
256
+ catch (err) {
257
+ this.emitHandlerError(pkt, err);
258
+ }
259
+ })();
260
+ continue;
261
+ }
262
+ try {
263
+ await handler(pkt);
264
+ }
265
+ catch (err) {
266
+ // Handler error → emit PipelineErrorPacket on Critical.
267
+ // Continue processing other handlers — don't abort the bus.
268
+ this.emitHandlerError(pkt, err);
269
+ }
270
+ }
271
+ }
272
+ emitHandlerError(pkt, err) {
273
+ const errorPkt = {
274
+ kind: "pipeline.error",
275
+ contextId: pkt.contextId,
276
+ timestampMs: Date.now(),
277
+ component: "pipeline",
278
+ category: ErrorCategory.InternalFault,
279
+ cause: err instanceof Error ? err : new Error(String(err)),
280
+ isRecoverable: true,
281
+ };
282
+ this.push(Route.Critical, errorPkt);
283
+ }
284
+ /** Synchronous dispatch for drain-on-stop. Swallows errors. */
285
+ async dispatchSync(pkt) {
286
+ try {
287
+ await this.dispatch(pkt);
288
+ }
289
+ catch {
290
+ // Drain phase — silence errors
291
+ }
292
+ }
293
+ }
294
+ //# sourceMappingURL=pipeline-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-bus.js","sourceRoot":"","sources":["../src/pipeline-bus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type { PipelineBus } from "./pipeline-bus.js";
2
+ export type EndpointingOwner = "provider_stt" | "smart_turn";
3
+ export interface EndpointingCapability {
4
+ readonly owner: EndpointingOwner;
5
+ readonly disableConfig?: PluginConfig;
6
+ }
7
+ /**
8
+ * Vendor-agnostic mid-stream STT reconfiguration. Per-turn reconfigure is a COMMODITY STT capability
9
+ * (Deepgram Flux `Configure`, AssemblyAI `UpdateConfiguration`, Speechmatics `SetRecognitionConfig`) —
10
+ * the value of THIS seam is normalizing those differing wire shapes behind one interface so an
11
+ * InteractionPolicy can actuate any STT without vendor-specific plumbing. All fields optional; a plugin
12
+ * applies what it supports and ignores the rest (best-effort — e.g. Deepgram ignores `contextText`).
13
+ */
14
+ export interface SttReconfigurePartial {
15
+ readonly keyterms?: readonly string[];
16
+ readonly eotThreshold?: number;
17
+ readonly eagerEotThreshold?: number;
18
+ readonly eotTimeoutMs?: number;
19
+ /** Silence-based endpointing (ms). Nova-style; Flux may ignore (it uses eotTimeoutMs). */
20
+ readonly endpointingMs?: number;
21
+ readonly vadThreshold?: number;
22
+ readonly languageHints?: readonly string[];
23
+ /**
24
+ * Hard recognition-language switch (e.g. "en-US" → "es-ES", or Nova-3 "multi" for code-switch).
25
+ * Distinct from `languageHints` (soft bias): this changes the recognizer's language. Providers
26
+ * that carry language in the connection URL (Nova) reconnect to apply it; model-fixed providers
27
+ * (Flux, `flux-general-en`) ignore it and rely on `languageHints`.
28
+ */
29
+ readonly language?: string;
30
+ /** AssemblyAI-style agent-context biasing (the agent's own prior reply). Ignored where unsupported. */
31
+ readonly contextText?: string;
32
+ }
33
+ export interface SttReconfigure {
34
+ reconfigure(partial: SttReconfigurePartial): void;
35
+ }
36
+ export interface VoicePlugin {
37
+ readonly endpointingCapability?: EndpointingCapability;
38
+ /** Present when the STT supports mid-stream reconfiguration (see {@link SttReconfigure}). */
39
+ readonly sttReconfigure?: SttReconfigure;
40
+ /**
41
+ * Initialize the plugin. Called during the init chain.
42
+ * Connect to provider, start streams, register bus handlers if needed.
43
+ *
44
+ * @param bus — The session's PipelineBus. Push all output packets here.
45
+ * @param config — Plugin-specific configuration (API keys, model IDs, etc.).
46
+ */
47
+ initialize(bus: PipelineBus, config: PluginConfig): Promise<void>;
48
+ /**
49
+ * Best-effort warm of remote/expensive resources (open connections, wake a scaled-to-zero
50
+ * endpoint). Called AFTER initialize, before the first turn. Must not throw fatally — swallow
51
+ * errors internally. Optional; plugins with nothing to warm omit it.
52
+ */
53
+ prewarm?(): Promise<void>;
54
+ /**
55
+ * Tear down the plugin. Called during the finalize chain (reverse order).
56
+ * Close connections, flush buffers, release resources.
57
+ */
58
+ close(): Promise<void>;
59
+ }
60
+ /**
61
+ * Plugin configuration — a flat key-value bag.
62
+ * Plugins extract the keys they need (e.g., "api_key", "model_id", "voice_id").
63
+ */
64
+ export type PluginConfig = Record<string, unknown>;
65
+ /**
66
+ * Convenience: extract a string config value, throwing if missing.
67
+ */
68
+ export declare function requireStringConfig(config: PluginConfig, key: string): string;
69
+ /**
70
+ * Convenience: extract an optional string config value.
71
+ */
72
+ export declare function optionalStringConfig(config: PluginConfig, key: string): string | undefined;
73
+ //# sourceMappingURL=plugin-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-contract.d.ts","sourceRoot":"","sources":["../src/plugin-contract.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAMrD,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,YAAY,CAAC;AAE7D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,YAAY,CAAC;CACvC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,0FAA0F;IAC1F,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uGAAuG;IACvG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAEvD,6FAA6F;IAC7F,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IAEzC;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;;;OAIG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1B;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAMD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,MAAM,GACV,MAAM,CAMR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAKpB"}
@@ -0,0 +1,30 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // Syrinx Kernel v2 — Plugin Contract
4
+ //
5
+ // Every pipeline plugin (STT, TTS, VAD, EOS, Denoiser, Bridge) implements
6
+ // this interface. Plugins receive the PipelineBus on initialization and
7
+ // push all output (transcripts, audio, errors, events) into the bus.
8
+ //
9
+ // Breaking change from v0.1: plugins now accept PipelineBus directly.
10
+ // No callbacks, no adapters. Clean contract, one code path.
11
+ /**
12
+ * Convenience: extract a string config value, throwing if missing.
13
+ */
14
+ export function requireStringConfig(config, key) {
15
+ const value = config[key];
16
+ if (typeof value !== "string" || value.trim().length === 0) {
17
+ throw new Error(`Plugin config missing required key: ${key}`);
18
+ }
19
+ return value;
20
+ }
21
+ /**
22
+ * Convenience: extract an optional string config value.
23
+ */
24
+ export function optionalStringConfig(config, key) {
25
+ const value = config[key];
26
+ return typeof value === "string" && value.trim().length > 0
27
+ ? value
28
+ : undefined;
29
+ }
30
+ //# sourceMappingURL=plugin-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-contract.js","sourceRoot":"","sources":["../src/plugin-contract.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,qCAAqC;AACrC,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,qEAAqE;AACrE,EAAE;AACF,sEAAsE;AACtE,4DAA4D;AAqF5D;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAoB,EACpB,GAAW;IAEX,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,uCAAuC,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAoB,EACpB,GAAW;IAEX,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACzD,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { InteractionDecision, InteractionObservation, InteractionPolicy } from "../interaction-policy.js";
2
+ /**
3
+ * The front owns full-duplex interaction (turn-taking, barge-in, backchannels). This policy takes no
4
+ * action — it observes only, so the coordinator drives nothing and the front's own decisions (surfaced
5
+ * by its adapter/bridge as interrupt.detected / eos.turn_complete) stand. RFC InteractionPolicy REQ-4.
6
+ */
7
+ export declare class DeferInteractionPolicy implements InteractionPolicy {
8
+ observe(_obs: InteractionObservation): readonly InteractionDecision[];
9
+ reset(_contextId: string): void;
10
+ }
11
+ //# sourceMappingURL=defer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defer.d.ts","sourceRoot":"","sources":["../../src/policies/defer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE/G;;;;GAIG;AACH,qBAAa,sBAAuB,YAAW,iBAAiB;IAC9D,OAAO,CAAC,IAAI,EAAE,sBAAsB,GAAG,SAAS,mBAAmB,EAAE;IAGrE,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAChC"}
@@ -0,0 +1,13 @@
1
+ // SPDX-License-Identifier: MIT
2
+ /**
3
+ * The front owns full-duplex interaction (turn-taking, barge-in, backchannels). This policy takes no
4
+ * action — it observes only, so the coordinator drives nothing and the front's own decisions (surfaced
5
+ * by its adapter/bridge as interrupt.detected / eos.turn_complete) stand. RFC InteractionPolicy REQ-4.
6
+ */
7
+ export class DeferInteractionPolicy {
8
+ observe(_obs) {
9
+ return [];
10
+ }
11
+ reset(_contextId) { }
12
+ }
13
+ //# sourceMappingURL=defer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defer.js","sourceRoot":"","sources":["../../src/policies/defer.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAI/B;;;;GAIG;AACH,MAAM,OAAO,sBAAsB;IACjC,OAAO,CAAC,IAA4B;QAClC,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,KAAK,CAAC,UAAkB,IAAS,CAAC;CACnC"}
@@ -0,0 +1,35 @@
1
+ import type { PipelineBus } from "../pipeline-bus.js";
2
+ import type { InteractionDecision, InteractionObservation, InteractionPolicy } from "../interaction-policy.js";
3
+ import { PrimarySpeakerGate } from "../primary-speaker-gate.js";
4
+ import { TtsPlayoutClock } from "../tts-playout-clock.js";
5
+ import { TurnArbiter } from "../turn-arbiter.js";
6
+ export interface RuleBasedInteractionPolicyDeps {
7
+ readonly bus: PipelineBus;
8
+ readonly primarySpeakerGate: PrimarySpeakerGate;
9
+ readonly ttsPlayout: TtsPlayoutClock;
10
+ readonly minInterruptionMs: number;
11
+ /** When true, the underlying TurnArbiter emits duck/resume signals during barge-in evaluation. */
12
+ readonly pauseThenResolveBargeIn?: boolean;
13
+ /** Tenant-level backchannel knobs. Default (undefined) = current behavior (`mm_hmm`). */
14
+ readonly backchannel?: {
15
+ readonly enabled?: boolean;
16
+ readonly cues?: readonly string[];
17
+ };
18
+ }
19
+ export declare class RuleBasedInteractionPolicy implements InteractionPolicy {
20
+ readonly arbiter: TurnArbiter;
21
+ private readonly ttsPlayout;
22
+ private readonly backchannelConfig;
23
+ private pending;
24
+ private bargeInAudioConsumed;
25
+ private delegateGapOpen;
26
+ private delegateCuePlayed;
27
+ private userSpeaking;
28
+ constructor(deps: RuleBasedInteractionPolicyDeps);
29
+ observe(obs: InteractionObservation): readonly InteractionDecision[];
30
+ private handleDelegateState;
31
+ private depsTtsActive;
32
+ reset(_contextId: string): void;
33
+ takeBargeInAudioConsumed(): boolean;
34
+ }
35
+ //# sourceMappingURL=rule-based.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-based.d.ts","sourceRoot":"","sources":["../../src/policies/rule-based.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/G,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,WAAW,8BAA8B;IAC7C,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,kGAAkG;IAClG,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C,yFAAyF;IACzF,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACnC,CAAC;CACH;AAED,qBAAa,0BAA2B,YAAW,iBAAiB;IAClE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgD;IAClF,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,YAAY,CAAS;gBAEjB,IAAI,EAAE,8BAA8B;IAahD,OAAO,CAAC,GAAG,EAAE,sBAAsB,GAAG,SAAS,mBAAmB,EAAE;IAiEpE,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,aAAa;IAIrB,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAO/B,wBAAwB,IAAI,OAAO;CAKpC"}