@craftedxp/voice-js 0.10.0 → 0.12.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.
- package/README.md +21 -19
- package/dist/assistant.d.mts +3 -3
- package/dist/assistant.d.ts +3 -3
- package/dist/assistant.js +210 -22
- package/dist/assistant.js.map +1 -1
- package/dist/assistant.mjs +2 -1
- package/dist/browser.d.mts +3 -3
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +201 -34
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +3 -2
- package/dist/browser.mjs.map +1 -1
- package/dist/{chunk-3BYNC25N.mjs → chunk-C2U6V5NI.mjs} +191 -23
- package/dist/chunk-C2U6V5NI.mjs.map +1 -0
- package/dist/chunk-OS2GZAR7.mjs +18 -0
- package/dist/chunk-OS2GZAR7.mjs.map +1 -0
- package/dist/{chunk-LV7JGPYW.mjs → chunk-VSY43NGD.mjs} +6 -13
- package/dist/chunk-VSY43NGD.mjs.map +1 -0
- package/dist/{config-D2TbvIqT.d.mts → config-DfbPB1Tq.d.mts} +22 -3
- package/dist/{config-D2TbvIqT.d.ts → config-DfbPB1Tq.d.ts} +22 -3
- package/dist/embed.iife.js +42 -5
- package/dist/{incomingCall-CfRRzj2P.d.ts → incomingCall-2B3XB8pM.d.mts} +3 -1
- package/dist/{incomingCall-CfRRzj2P.d.mts → incomingCall-CYmeQkIw.d.ts} +3 -1
- package/dist/node.d.mts +26 -3
- package/dist/node.d.ts +26 -3
- package/dist/node.js +11 -2
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +11 -2
- package/dist/node.mjs.map +1 -1
- package/dist/room.js +15 -12
- package/dist/room.js.map +1 -1
- package/dist/room.mjs +2 -1
- package/dist/transcribe.d.mts +2 -2
- package/dist/transcribe.d.ts +2 -2
- package/dist/transcribe.js +209 -21
- package/dist/transcribe.js.map +1 -1
- package/dist/transcribe.mjs +2 -1
- package/dist/transcribe.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3BYNC25N.mjs.map +0 -1
- package/dist/chunk-LV7JGPYW.mjs.map +0 -1
package/dist/transcribe.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { f as CallState,
|
|
1
|
+
import { n as StartCallOptions, V as VoiceClientConfig } from './config-DfbPB1Tq.mjs';
|
|
2
|
+
export { f as CallState, p as TranscriptEntry } from './config-DfbPB1Tq.mjs';
|
|
3
3
|
|
|
4
4
|
interface DictationSession {
|
|
5
5
|
end: () => void;
|
package/dist/transcribe.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { f as CallState,
|
|
1
|
+
import { n as StartCallOptions, V as VoiceClientConfig } from './config-DfbPB1Tq.js';
|
|
2
|
+
export { f as CallState, p as TranscriptEntry } from './config-DfbPB1Tq.js';
|
|
3
3
|
|
|
4
4
|
interface DictationSession {
|
|
5
5
|
end: () => void;
|
package/dist/transcribe.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/transcribe.ts
|
|
@@ -504,7 +514,8 @@ function buildWsUrl(args) {
|
|
|
504
514
|
const base = new URL(args.apiBase);
|
|
505
515
|
const proto = base.protocol === "https:" ? "wss:" : "ws:";
|
|
506
516
|
const bargeQS = args.bargeIn === false ? "&barge=off" : "";
|
|
507
|
-
|
|
517
|
+
const mediaQS = args.media === "livekit" ? "&media=livekit" : "";
|
|
518
|
+
return `${proto}//${base.host}/v1/agents/${encodeURIComponent(args.agentId)}/call?token=${encodeURIComponent(args.token)}${bargeQS}${mediaQS}`;
|
|
508
519
|
}
|
|
509
520
|
|
|
510
521
|
// src/clientTools.ts
|
|
@@ -658,7 +669,75 @@ var createClientMarksBuffer = (args) => {
|
|
|
658
669
|
};
|
|
659
670
|
};
|
|
660
671
|
|
|
672
|
+
// src/livekit/createLiveKitMedia.ts
|
|
673
|
+
var toError = (e) => e instanceof Error ? e : new Error(String(e));
|
|
674
|
+
function createLiveKitMedia(lk, opts = {}) {
|
|
675
|
+
let room = null;
|
|
676
|
+
let audioEl = null;
|
|
677
|
+
let muted = false;
|
|
678
|
+
const attach = (track) => {
|
|
679
|
+
if (track.kind !== lk.Track.Kind.Audio) return;
|
|
680
|
+
if (typeof document === "undefined") return;
|
|
681
|
+
if (!audioEl) {
|
|
682
|
+
audioEl = document.createElement("audio");
|
|
683
|
+
audioEl.autoplay = true;
|
|
684
|
+
audioEl.style.display = "none";
|
|
685
|
+
document.body.appendChild(audioEl);
|
|
686
|
+
}
|
|
687
|
+
track.attach(audioEl);
|
|
688
|
+
};
|
|
689
|
+
return {
|
|
690
|
+
connect: async (url, token) => {
|
|
691
|
+
const r = new lk.Room({ adaptiveStream: false, dynacast: false });
|
|
692
|
+
room = r;
|
|
693
|
+
r.on(lk.RoomEvent.TrackSubscribed, (track) => attach(track));
|
|
694
|
+
await r.connect(url, token);
|
|
695
|
+
if (room !== r) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
await r.localParticipant.setMicrophoneEnabled(!muted);
|
|
699
|
+
if (room !== r) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
try {
|
|
703
|
+
await r.startAudio();
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
disconnect: () => {
|
|
708
|
+
const r = room;
|
|
709
|
+
room = null;
|
|
710
|
+
if (r) void r.disconnect().catch(() => void 0);
|
|
711
|
+
audioEl?.remove();
|
|
712
|
+
audioEl = null;
|
|
713
|
+
},
|
|
714
|
+
setMuted: (m) => {
|
|
715
|
+
muted = m;
|
|
716
|
+
const r = room;
|
|
717
|
+
if (!r) return;
|
|
718
|
+
void r.localParticipant.setMicrophoneEnabled(!m).catch((e) => opts.onError?.(toError(e)));
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
661
723
|
// src/VoiceClient.ts
|
|
724
|
+
var parseMediaFrame = (raw) => {
|
|
725
|
+
if (!raw.includes('"livekit_')) return null;
|
|
726
|
+
try {
|
|
727
|
+
const m = JSON.parse(raw);
|
|
728
|
+
if (m.type === "livekit_join" && typeof m.url === "string" && typeof m.token === "string") {
|
|
729
|
+
return { type: "livekit_join", url: m.url, token: m.token };
|
|
730
|
+
}
|
|
731
|
+
if (m.type === "livekit_fallback") {
|
|
732
|
+
return {
|
|
733
|
+
type: "livekit_fallback",
|
|
734
|
+
reason: typeof m.reason === "string" ? m.reason : void 0
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
} catch {
|
|
738
|
+
}
|
|
739
|
+
return null;
|
|
740
|
+
};
|
|
662
741
|
var BrowserVoiceClient = class {
|
|
663
742
|
constructor(args) {
|
|
664
743
|
this.rws = null;
|
|
@@ -670,6 +749,9 @@ var BrowserVoiceClient = class {
|
|
|
670
749
|
this.startedAt = null;
|
|
671
750
|
this.endedFired = false;
|
|
672
751
|
this.lastError = null;
|
|
752
|
+
this.mediaMode = "ws";
|
|
753
|
+
this.lkMedia = null;
|
|
754
|
+
this.lkJoinSeen = false;
|
|
673
755
|
this.end = () => {
|
|
674
756
|
this.teardown("user_hangup");
|
|
675
757
|
};
|
|
@@ -677,11 +759,73 @@ var BrowserVoiceClient = class {
|
|
|
677
759
|
if (this.muted) return;
|
|
678
760
|
this.muted = true;
|
|
679
761
|
this.capture?.mute(true);
|
|
762
|
+
this.lkMedia?.setMuted(true);
|
|
680
763
|
};
|
|
681
764
|
this.unmute = () => {
|
|
682
765
|
if (!this.muted) return;
|
|
683
766
|
this.muted = false;
|
|
684
767
|
this.capture?.mute(false);
|
|
768
|
+
this.lkMedia?.setMuted(false);
|
|
769
|
+
};
|
|
770
|
+
this.ensureWsPlayback = async () => {
|
|
771
|
+
if (this.playback) return;
|
|
772
|
+
const createPlayback = this.args.audio?.createPlayback ?? createAudioPlayback;
|
|
773
|
+
this.playback = createPlayback({
|
|
774
|
+
onVolume: (v) => {
|
|
775
|
+
this.outputVolume = v;
|
|
776
|
+
this.args.options.onVolume?.({ input: this.inputVolume, output: v });
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
try {
|
|
780
|
+
await this.playback.resume();
|
|
781
|
+
} catch {
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
// ---------------------------------------------------------------
|
|
785
|
+
// LiveKit media lifecycle (phase 35)
|
|
786
|
+
// ---------------------------------------------------------------
|
|
787
|
+
this.joinLiveKit = async (url, token) => {
|
|
788
|
+
this.lkJoinSeen = true;
|
|
789
|
+
const lk = this.args.media?.lk;
|
|
790
|
+
if (!lk) {
|
|
791
|
+
this.fallbackToWs("livekit-client unavailable");
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const media = createLiveKitMedia(lk, {
|
|
795
|
+
onError: (e) => this.emitError({ code: "mic_start_failed", message: e.message })
|
|
796
|
+
});
|
|
797
|
+
this.lkMedia = media;
|
|
798
|
+
if (this.muted) media.setMuted(true);
|
|
799
|
+
try {
|
|
800
|
+
await media.connect(url, token);
|
|
801
|
+
if (this.lkMedia !== media) return;
|
|
802
|
+
this.mediaMode = "livekit";
|
|
803
|
+
this.rws?.send(JSON.stringify({ type: "livekit_ready" }));
|
|
804
|
+
} catch (err) {
|
|
805
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
806
|
+
media.disconnect();
|
|
807
|
+
if (this.lkMedia === media) this.lkMedia = null;
|
|
808
|
+
this.rws?.send(JSON.stringify({ type: "livekit_failed", reason }));
|
|
809
|
+
this.fallbackToWs(reason);
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
// Idempotent: whichever side gives up first (client join error, server
|
|
813
|
+
// livekit_fallback, `connected` without a join) lands here once.
|
|
814
|
+
this.fallbackToWs = (reason) => {
|
|
815
|
+
if (this.mediaMode === "ws") return;
|
|
816
|
+
this.mediaMode = "ws";
|
|
817
|
+
this.lkMedia?.disconnect();
|
|
818
|
+
this.lkMedia = null;
|
|
819
|
+
void this.ensureWsPlayback();
|
|
820
|
+
void this.startCapture();
|
|
821
|
+
this.args.options.onTransportChange?.({ requested: "livekit", actual: "ws", reason });
|
|
822
|
+
};
|
|
823
|
+
this.resetLiveKitForReconnect = () => {
|
|
824
|
+
if (this.args.media?.kind !== "livekit") return;
|
|
825
|
+
this.lkMedia?.disconnect();
|
|
826
|
+
this.lkMedia = null;
|
|
827
|
+
this.lkJoinSeen = false;
|
|
828
|
+
this.mediaMode = "livekit-pending";
|
|
685
829
|
};
|
|
686
830
|
// ---------------------------------------------------------------
|
|
687
831
|
// Internal
|
|
@@ -702,17 +846,27 @@ var BrowserVoiceClient = class {
|
|
|
702
846
|
this.handleSocketEvent = (ev) => {
|
|
703
847
|
switch (ev.type) {
|
|
704
848
|
case "open":
|
|
705
|
-
void this.startCapture();
|
|
849
|
+
if (this.mediaMode === "ws") void this.startCapture();
|
|
706
850
|
break;
|
|
707
851
|
case "reconnected":
|
|
708
852
|
this.proto.transcript = [];
|
|
709
853
|
this.proto.agentBubbleId = null;
|
|
710
854
|
this.args.options.onTranscript?.(this.proto.transcript);
|
|
711
|
-
|
|
855
|
+
this.resetLiveKitForReconnect();
|
|
856
|
+
if (this.mediaMode === "ws") void this.startCapture();
|
|
712
857
|
this.setState("listening");
|
|
713
858
|
break;
|
|
714
859
|
case "message":
|
|
715
860
|
if (typeof ev.data === "string") {
|
|
861
|
+
const media = parseMediaFrame(ev.data);
|
|
862
|
+
if (media?.type === "livekit_join") {
|
|
863
|
+
if (this.mediaMode === "livekit-pending") void this.joinLiveKit(media.url, media.token);
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
if (media?.type === "livekit_fallback") {
|
|
867
|
+
this.fallbackToWs(media.reason ?? "server_fallback");
|
|
868
|
+
break;
|
|
869
|
+
}
|
|
716
870
|
handleServerMessage(ev.data, this.proto, {
|
|
717
871
|
onState: this.setState,
|
|
718
872
|
onTranscript: (entries) => this.args.options.onTranscript?.(entries),
|
|
@@ -729,7 +883,12 @@ var BrowserVoiceClient = class {
|
|
|
729
883
|
if (typeof seq === "number") this.marks.onAgentTurnEnd(seq);
|
|
730
884
|
},
|
|
731
885
|
onCallEnd: (reason) => this.teardown(reason),
|
|
732
|
-
onConnected: () =>
|
|
886
|
+
onConnected: () => {
|
|
887
|
+
if (this.mediaMode === "livekit-pending" && !this.lkJoinSeen) {
|
|
888
|
+
this.fallbackToWs("server_declined");
|
|
889
|
+
}
|
|
890
|
+
this.sendClientToolsRegister();
|
|
891
|
+
},
|
|
733
892
|
onClientToolCall: (frame) => dispatchClientToolCall(
|
|
734
893
|
(f) => this.rws?.send(JSON.stringify(f)),
|
|
735
894
|
this.args.options.clientTools ?? {},
|
|
@@ -737,6 +896,8 @@ var BrowserVoiceClient = class {
|
|
|
737
896
|
)
|
|
738
897
|
});
|
|
739
898
|
} else {
|
|
899
|
+
if (this.mediaMode === "livekit") break;
|
|
900
|
+
if (!this.playback) void this.ensureWsPlayback();
|
|
740
901
|
this.marks.markFirstAudibleOutput();
|
|
741
902
|
this.playback?.enqueue(ev.data);
|
|
742
903
|
}
|
|
@@ -754,7 +915,8 @@ var BrowserVoiceClient = class {
|
|
|
754
915
|
};
|
|
755
916
|
this.startCapture = async () => {
|
|
756
917
|
if (this.capture?.isCapturing()) return;
|
|
757
|
-
this.
|
|
918
|
+
const createCapture = this.args.audio?.createCapture ?? createAudioCapture;
|
|
919
|
+
this.capture = createCapture({
|
|
758
920
|
onChunk: (pcm) => {
|
|
759
921
|
this.marks.markFirstOutboundAudio();
|
|
760
922
|
this.rws?.send(pcm);
|
|
@@ -783,6 +945,8 @@ var BrowserVoiceClient = class {
|
|
|
783
945
|
}
|
|
784
946
|
this.capture?.stop();
|
|
785
947
|
this.capture = null;
|
|
948
|
+
this.lkMedia?.disconnect();
|
|
949
|
+
this.lkMedia = null;
|
|
786
950
|
this.playback?.close();
|
|
787
951
|
this.playback = null;
|
|
788
952
|
try {
|
|
@@ -835,28 +999,17 @@ var BrowserVoiceClient = class {
|
|
|
835
999
|
async start() {
|
|
836
1000
|
this.setState("connecting");
|
|
837
1001
|
this.startedAt = Date.now();
|
|
1002
|
+
this.mediaMode = this.args.media?.kind === "livekit" ? "livekit-pending" : "ws";
|
|
838
1003
|
const url = buildWsUrl({
|
|
839
1004
|
apiBase: this.args.config.apiBase,
|
|
840
1005
|
agentId: this.args.options.agentId,
|
|
841
1006
|
token: this.args.token,
|
|
842
|
-
bargeIn: this.args.options.bargeIn
|
|
843
|
-
|
|
844
|
-
this.playback = createAudioPlayback({
|
|
845
|
-
onVolume: (v) => {
|
|
846
|
-
this.outputVolume = v;
|
|
847
|
-
this.args.options.onVolume?.({ input: this.inputVolume, output: v });
|
|
848
|
-
}
|
|
1007
|
+
bargeIn: this.args.options.bargeIn,
|
|
1008
|
+
media: this.mediaMode === "ws" ? void 0 : "livekit"
|
|
849
1009
|
});
|
|
850
|
-
|
|
851
|
-
await this.playback.resume();
|
|
852
|
-
} catch {
|
|
853
|
-
}
|
|
1010
|
+
if (this.mediaMode === "ws") await this.ensureWsPlayback();
|
|
854
1011
|
this.rws = createReconnectingWebSocket(
|
|
855
|
-
{
|
|
856
|
-
url,
|
|
857
|
-
wsFactory: this.args.wsFactory,
|
|
858
|
-
maxRetries: 3
|
|
859
|
-
},
|
|
1012
|
+
{ url, wsFactory: this.args.wsFactory, maxRetries: 3 },
|
|
860
1013
|
(ev) => this.handleSocketEvent(ev)
|
|
861
1014
|
);
|
|
862
1015
|
}
|
|
@@ -1041,6 +1194,20 @@ async function createWebRtcCall(opts) {
|
|
|
1041
1194
|
};
|
|
1042
1195
|
}
|
|
1043
1196
|
|
|
1197
|
+
// src/livekit/loadLiveKit.ts
|
|
1198
|
+
var lkPromise;
|
|
1199
|
+
var loadLiveKit = (requiredBy = "this feature") => {
|
|
1200
|
+
lkPromise ?? (lkPromise = import("livekit-client").catch((e) => {
|
|
1201
|
+
lkPromise = void 0;
|
|
1202
|
+
const wrapped = new Error(
|
|
1203
|
+
`${requiredBy} requires the optional peer dependency 'livekit-client' \u2014 npm install livekit-client`
|
|
1204
|
+
);
|
|
1205
|
+
wrapped.cause = e;
|
|
1206
|
+
throw wrapped;
|
|
1207
|
+
}));
|
|
1208
|
+
return lkPromise;
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1044
1211
|
// src/textSession.ts
|
|
1045
1212
|
async function startTextSession(opts) {
|
|
1046
1213
|
const f = opts.fetch ?? fetch;
|
|
@@ -1171,6 +1338,27 @@ var AssistantVoiceFactory = class {
|
|
|
1171
1338
|
clientTools: options.clientTools
|
|
1172
1339
|
});
|
|
1173
1340
|
}
|
|
1341
|
+
if (resolved.transport === "livekit") {
|
|
1342
|
+
let lk = null;
|
|
1343
|
+
let loadError = "";
|
|
1344
|
+
try {
|
|
1345
|
+
lk = await loadLiveKit("transport 'livekit'");
|
|
1346
|
+
} catch (e) {
|
|
1347
|
+
loadError = e instanceof Error ? e.message : String(e);
|
|
1348
|
+
}
|
|
1349
|
+
if (!lk) {
|
|
1350
|
+
options.onTransportChange?.({ requested: "livekit", actual: "ws", reason: loadError });
|
|
1351
|
+
}
|
|
1352
|
+
const client2 = new BrowserVoiceClient({
|
|
1353
|
+
config: this.config,
|
|
1354
|
+
options: { ...options, context, metadata },
|
|
1355
|
+
token: resolved.token,
|
|
1356
|
+
wsFactory: browserWsFactory,
|
|
1357
|
+
media: lk ? { kind: "livekit", lk } : void 0
|
|
1358
|
+
});
|
|
1359
|
+
await client2.start();
|
|
1360
|
+
return client2;
|
|
1361
|
+
}
|
|
1174
1362
|
const client = new BrowserVoiceClient({
|
|
1175
1363
|
config: this.config,
|
|
1176
1364
|
// Carry merged context/metadata through to startCall so server can
|