@alexkroman1/aai-ui 1.16.0 → 3.0.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 (49) hide show
  1. package/dist/_module-url-C_4gRVL0.js +15 -0
  2. package/dist/audio.d.ts +85 -11
  3. package/dist/audio.js +112 -83
  4. package/dist/chat-view-C1oJxsWz.js +129 -0
  5. package/dist/client-config.d.ts +6 -6
  6. package/dist/components/chat-view.d.ts +1 -8
  7. package/dist/components/chat-view.js +2 -2
  8. package/dist/components/console-shell.d.ts +37 -0
  9. package/dist/components/controls.js +1 -1
  10. package/dist/components/url-chips.d.ts +1 -4
  11. package/dist/context.d.ts +1 -1
  12. package/dist/context.js +1 -4
  13. package/dist/{controls-BbZcmnJf.js → controls-DV368uhb.js} +2 -5
  14. package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
  15. package/dist/default-client/assets/audio-BGHiiDY_.js +1 -0
  16. package/dist/default-client/assets/capture-processor-BLPsqnGl.js +90 -0
  17. package/dist/default-client/assets/{index-BunwIXSP.js → index-DXf7-M0Q.js} +156 -36
  18. package/dist/default-client/assets/index-Dv-Q5VRL.css +2 -0
  19. package/dist/default-client/assets/playback-processor-B_T_1KQP.js +278 -0
  20. package/dist/default-client/index.html +2 -2
  21. package/dist/define-client-Cyf1jqAl.js +150 -0
  22. package/dist/define-client.d.ts +0 -9
  23. package/dist/define-client.js +1 -1
  24. package/dist/index.d.ts +2 -5
  25. package/dist/index.js +7 -5
  26. package/dist/{session-core-B64kau_v.js → session-core-BM3WHeoY.js} +36 -128
  27. package/dist/session-core-messages.d.ts +0 -4
  28. package/dist/session-core-types.d.ts +1 -27
  29. package/dist/session-core.js +1 -1
  30. package/dist/types.d.ts +24 -1
  31. package/dist/types.js +32 -2
  32. package/dist/worklets/_module-url.d.ts +10 -0
  33. package/dist/worklets/capture-processor.d.ts +3 -3
  34. package/dist/worklets/capture-processor.js +35 -52
  35. package/dist/worklets/playback-processor.d.ts +3 -3
  36. package/dist/worklets/playback-processor.js +149 -26
  37. package/package.json +2 -2
  38. package/dist/chat-view-gi6FccZq.js +0 -193
  39. package/dist/components/sync-chat-view.d.ts +0 -18
  40. package/dist/components/text-controls.d.ts +0 -14
  41. package/dist/default-client/assets/audio-DNDZgEZp.js +0 -1
  42. package/dist/default-client/assets/capture-processor-UlKEKyIW.js +0 -108
  43. package/dist/default-client/assets/index-BogmeUln.css +0 -2
  44. package/dist/default-client/assets/playback-processor-C5HVRVbu.js +0 -156
  45. package/dist/define-client-DdpijqAu.js +0 -906
  46. package/dist/session-core-upload.d.ts +0 -16
  47. package/dist/sync-mic.d.ts +0 -93
  48. package/dist/sync-session.d.ts +0 -49
  49. package/dist/sync-vad.d.ts +0 -54
package/dist/context.js CHANGED
@@ -36,10 +36,7 @@ function useSession() {
36
36
  resetState: core.resetState,
37
37
  reset: core.reset,
38
38
  disconnect: core.disconnect,
39
- toggle: core.toggle,
40
- startRecording: core.startRecording,
41
- stopRecording: core.stopRecording,
42
- sendAudioFile: core.sendAudioFile
39
+ toggle: core.toggle
43
40
  }), [snapshot, core]);
44
41
  }
45
42
  /**
@@ -14,9 +14,6 @@ const COPIED_FEEDBACK_MS = 1500;
14
14
  * The label is what makes a pair of these readable — on its own a bare URL
15
15
  * leaves you guessing whether it's the page or the socket.
16
16
  *
17
- * Exported for the sync-transport shell, which labels its HTTP endpoint
18
- * without a session snapshot to read from.
19
- *
20
17
  * @internal
21
18
  */
22
19
  function UrlChip({ label, url, hint, testId, className }) {
@@ -91,7 +88,7 @@ function ApiUrlChip({ className }) {
91
88
  * The UI and API URLs side by side. They answer the same question — "how do I
92
89
  * reach this agent?" — so they belong together and each needs its label to be
93
90
  * told apart. Rendered by the default shell in every session mode (S2S,
94
- * pipeline, text-only).
91
+ * pipeline).
95
92
  *
96
93
  * @public
97
94
  */
@@ -140,4 +137,4 @@ const Controls = memo(function Controls({ className }) {
140
137
  });
141
138
  });
142
139
  //#endregion
143
- export { UrlChip as a, UiUrlChip as i, ApiUrlChip as n, SessionUrlChips as r, Controls as t };
140
+ export { UiUrlChip as i, ApiUrlChip as n, SessionUrlChips as r, Controls as t };
@@ -0,0 +1 @@
1
+ function e(e){return URL.createObjectURL(new Blob([e],{type:`application/javascript`}))}export{e as t};
@@ -0,0 +1 @@
1
+ import{a as e,o as t,t as n}from"./index-DXf7-M0Q.js";function r(e){let t=new Int16Array(e.length),n=0;for(let r of e){let e=Math.max(-1,Math.min(1,r));t[n++]=e<0?e*32768:e*32767}return t}function i(e,t,n){if(e!==t)throw Error(`Browser refused the ${n} sample rate: asked for ${t} Hz, got ${e} Hz`)}function a(e){e.then(e=>{for(let t of e.getTracks())t.stop()}).catch(()=>{})}function o(e,t,n){let r=new AudioWorkletNode(e,`capture-processor`,{channelCount:1,channelCountMode:`explicit`}),i=null;return r.port.onmessage=e=>{let r=e.data;r.event===`chunk`&&r.buffer?t(r.buffer):r.event===`silent`?n?.():r.event===`stopped`&&(i?.(),i=null)},{node:r,start(){r.port.postMessage({event:`start`})},stop(){return new Promise(e=>{let t=setTimeout(e,250);i=()=>{clearTimeout(t),e()},r.port.postMessage({event:`stop`})})}}}async function s(r){let{sttSampleRate:s,ttsSampleRate:c,captureWorkletSrc:l,playbackWorkletSrc:u,onMicData:d,onError:f,onPlaybackStats:p,onMicSilent:m}=r,h=new AudioContext({sampleRate:c,latencyHint:`playback`}),g=s===c,_=g?h:new AudioContext({sampleRate:s,latencyHint:`interactive`});async function v(){let e=g?[h]:[h,_];await Promise.all(e.map(e=>e.close().catch(e=>{console.warn(`AudioContext close failed:`,e)})))}let y=navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...n}}),b;try{[b]=await Promise.all([y,h.resume(),_.resume(),_.audioWorklet.addModule(l),h.audioWorklet.addModule(u)]),i(_.sampleRate,s,`capture`),i(h.sampleRate,c,`playback`)}catch(e){throw a(y),await v(),e}let x=_.createMediaStreamSource(b),S=o(_,d,m);x.connect(S.node),S.node.onprocessorerror=()=>{let e=Error(`Audio capture worklet crashed`);console.error(`[aai-ui]`,e.message),f?.(e)},S.start();let C=null,w=null,T=new AbortController;function E(){if(C)return C;let e=new AudioWorkletNode(h,`playback-processor`);return e.connect(h.destination),e.port.onmessage=e=>{if(e.data.event===`stop`){let t=e.data.stats;if(t&&t.concealedSamples>0&&p?.(t),e.data.reason===`interrupt`)return;w?.(),w=null}},e.onprocessorerror=()=>{let e=Error(`Audio playback worklet crashed`);console.error(`[aai-ui]`,e.message),w?.(),w=null,f?.(e)},C=e,e}let D={enqueue(e){T.signal.aborted||e.byteLength!==0&&E().port.postMessage({event:`write`,buffer:new Uint8Array(e)},[e])},done(){return!C||(C.port.postMessage({event:`done`}),h.state!==`running`)?Promise.resolve():new Promise(n=>{w?.();let r=()=>{clearInterval(i),clearTimeout(a),w===r&&(w=null),n()},i=setInterval(()=>{h.state!==`running`&&r()},t),a=setTimeout(r,e);w=r})},flush(){C&&(w?.(),w=null,C.port.postMessage({event:`interrupt`}))},async close(){if(!T.signal.aborted){T.abort(),await S.stop(),x.disconnect(),S.node.disconnect(),C&&C.disconnect();for(let e of b.getTracks())e.stop();await v()}},async[Symbol.asyncDispose](){await D.close()}};return D}export{i as assertGranted,o as createCaptureNode,s as createVoiceIO,r as floatToPcm16,a as releaseStreamOnFailure};
@@ -0,0 +1,90 @@
1
+ import{n as e,r as t}from"./index-DXf7-M0Q.js";import{t as n}from"./_module-url-BX0RuRU2.js";var r=n(`
2
+ class CaptureProcessor extends AudioWorkletProcessor {
3
+ constructor(options) {
4
+ super();
5
+ this.recording = false;
6
+ const opts = options.processorOptions || {};
7
+ // The context runs at the STT rate, so this is both the input and the
8
+ // output rate — there is nothing to convert.
9
+ this.rate = opts.sampleRate || sampleRate;
10
+ // Int16 accumulation buffer: flushed to the main thread as one transferred
11
+ // ArrayBuffer once ~bufferSeconds of samples are batched. Sized 2x the
12
+ // flush target so a whole render quantum always fits before flushing.
13
+ this.targetSamples = Math.max(1, Math.round(this.rate * (opts.bufferSeconds || ${e})));
14
+ this.pending = new Int16Array(this.targetSamples * 2);
15
+ this.pendingLen = 0;
16
+ // Dead-mic probe: samples left to inspect before concluding the device
17
+ // delivers nothing but digital silence. Only consumed while recording, so
18
+ // the cost disappears after the window (or after the first real sample).
19
+ this.probeSamplesLeft = Math.round(
20
+ (this.rate * (opts.silenceProbeMs ?? ${t})) / 1000,
21
+ );
22
+ this.port.onmessage = (e) => {
23
+ if (e.data.event === 'start') this.recording = true;
24
+ else if (e.data.event === 'stop') {
25
+ // Final flush so the tail of speech isn't dropped on close, then ack
26
+ // so the host knows the tail chunk (if any) has been posted and it is
27
+ // safe to tear the context down.
28
+ this.flush();
29
+ this.recording = false;
30
+ this.port.postMessage({ event: 'stopped' });
31
+ }
32
+ };
33
+ }
34
+
35
+ // Convert Float32 -> Int16 and append to the pending batch. Writes through
36
+ // an Int16Array directly (assignment truncates like DataView.setInt16).
37
+ accumulate(samples) {
38
+ let buf = this.pending;
39
+ if (this.pendingLen + samples.length > buf.length) {
40
+ // Defensive: only reachable if a render quantum outproduces the 1x
41
+ // headroom above the flush target (never with 128-sample quanta).
42
+ const grown = new Int16Array((this.pendingLen + samples.length) * 2);
43
+ grown.set(buf.subarray(0, this.pendingLen));
44
+ this.pending = grown;
45
+ buf = grown;
46
+ }
47
+ for (let i = 0; i < samples.length; i++) {
48
+ const s = Math.max(-1, Math.min(1, samples[i]));
49
+ buf[this.pendingLen++] = s < 0 ? s * 0x8000 : s * 0x7fff;
50
+ }
51
+ }
52
+
53
+ // Post the batched samples as one transferred ArrayBuffer and reset.
54
+ flush() {
55
+ if (this.pendingLen === 0) return;
56
+ const buffer = this.pending.buffer.slice(0, this.pendingLen * 2);
57
+ this.pendingLen = 0;
58
+ this.port.postMessage({ event: 'chunk', buffer }, [buffer]);
59
+ }
60
+
61
+ // Watch the first window of input for any nonzero sample. One is enough to
62
+ // prove the device is live — a real mic in a quiet room still carries a
63
+ // noise floor, so all-zeros means muted, wrong input, or no input at all.
64
+ probeForSilence(channel) {
65
+ for (let i = 0; i < channel.length; i++) {
66
+ if (channel[i] !== 0) {
67
+ this.probeSamplesLeft = 0;
68
+ return;
69
+ }
70
+ }
71
+ this.probeSamplesLeft -= channel.length;
72
+ if (this.probeSamplesLeft <= 0) {
73
+ this.port.postMessage({ event: 'silent' });
74
+ }
75
+ }
76
+
77
+ process(inputs) {
78
+ const input = inputs[0];
79
+ if (!input || !input[0] || !this.recording) return true;
80
+
81
+ if (this.probeSamplesLeft > 0) this.probeForSilence(input[0]);
82
+
83
+ this.accumulate(input[0]);
84
+ if (this.pendingLen >= this.targetSamples) this.flush();
85
+ return true;
86
+ }
87
+ }
88
+
89
+ registerProcessor('capture-processor', CaptureProcessor);
90
+ `);export{r as default};