dsh-live-voice 0.6.0 → 0.6.1
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 +1 -1
- package/lib/asr/asr.worker.js +1 -1
- package/lib/asr/manifest.json +1 -1
- package/lib/client.js +88 -34
- package/lib/server.js +2 -2
- package/lib/vad/manifest.json +2 -2
- package/lib/vad/vad.worker.js +1 -1
- package/package.json +7 -5
- package/src/app/client/apply.tsx +2 -2
- package/src/app/client/i18n/catalogs/base.ts +2 -0
- package/src/app/client/i18n/catalogs/en.ts +4 -0
- package/src/app/client/i18n/catalogs/es.ts +4 -0
- package/src/app/client/i18n/catalogs/fr.ts +4 -0
- package/src/app/client/i18n/catalogs/hi.ts +4 -0
- package/src/app/client/i18n/catalogs/pt-BR.ts +4 -0
- package/src/app/client/i18n/catalogs/zh.ts +4 -0
- package/src/modules/conversation/ARCHITECTURE.md +1 -1
- package/src/modules/conversation/models/meeting.ts +17 -4
- package/src/modules/core/coordinator.ts +21 -7
- package/src/modules/recognition/engines/whisper/WhisperRecognitionEngine.ts +7 -1
- package/src/modules/recording/ARCHITECTURE.md +1 -1
- package/src/modules/recording/RecordingRecognition.ts +44 -20
- package/src/modules/settings/ARCHITECTURE.md +1 -1
- package/src/modules/settings/services/pluginUpdate.ts +10 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Local-first speech-to-text (STT), text-to-speech (TTS), and meeting audio transc
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
Tested with **DeepSeek Harness v0.2.1-alpha.
|
|
7
|
+
Tested with **DeepSeek Harness v0.2.1-alpha.3**. The package manifest's `dshTestedVersion` field records the tested version.
|
|
8
8
|
|
|
9
9
|
Install the prebuilt package from npm; no source build is required. The Git repository contains source only.
|
|
10
10
|
|
package/lib/asr/asr.worker.js
CHANGED
|
@@ -36682,7 +36682,7 @@ async function createQwenRuntime(options) {
|
|
|
36682
36682
|
}
|
|
36683
36683
|
|
|
36684
36684
|
// src/modules/recognition/local/assets.ts
|
|
36685
|
-
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.6.
|
|
36685
|
+
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.6.1" : "test") + "/";
|
|
36686
36686
|
|
|
36687
36687
|
// src/modules/recognition/local/modelCatalog.ts
|
|
36688
36688
|
var browserAsrModels = [
|
package/lib/asr/manifest.json
CHANGED
package/lib/client.js
CHANGED
|
@@ -152,7 +152,7 @@ var SessionSpeechMemory = class {
|
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
// src/modules/recognition/vad/assets.ts
|
|
155
|
-
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.6.
|
|
155
|
+
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.6.1" : "test") + "/";
|
|
156
156
|
async function sileroVadAvailable(fetchImpl = globalThis.fetch) {
|
|
157
157
|
try {
|
|
158
158
|
const response = await fetchImpl(sileroVadAssetBase() + "availability", {
|
|
@@ -1734,8 +1734,10 @@ var en = {
|
|
|
1734
1734
|
"dsh-live-voice.commons.update.cancel": "Cancel",
|
|
1735
1735
|
"dsh-live-voice.commons.update.confirm": "Install Live Voice {version} in this DSH profile? Download requires network access. Restart DSH afterward when requested; nothing restarts automatically.",
|
|
1736
1736
|
"dsh-live-voice.commons.update.current": "This version is already installed. Restart DSH to use it.",
|
|
1737
|
+
"dsh-live-voice.commons.update.delayed": "The requested version is not available to this profile yet. It may still be publishing or be delayed by pnpm minimumReleaseAge. Wait, then check the DSH plugin manager; no security policy was bypassed.",
|
|
1737
1738
|
"dsh-live-voice.commons.update.done": "Live Voice {version} installed. Reload the page when ready.",
|
|
1738
1739
|
"dsh-live-voice.commons.update.failed": "Update failed. The release may not be published on npm yet. Check the DSH plugin manager.",
|
|
1740
|
+
"dsh-live-voice.commons.update.incompatible": "This release is incompatible with the running DSH version. Check the DSH plugin manager and use a compatible DSH/plugin version; no compatibility exemption was granted.",
|
|
1739
1741
|
"dsh-live-voice.commons.update.installing": "Updating\u2026",
|
|
1740
1742
|
"dsh-live-voice.commons.update.label": "Update available",
|
|
1741
1743
|
"dsh-live-voice.commons.update.link": "Update available: {version}. Open release",
|
|
@@ -2129,8 +2131,10 @@ var es = {
|
|
|
2129
2131
|
"dsh-live-voice.commons.update.cancel": "Cancelar",
|
|
2130
2132
|
"dsh-live-voice.commons.update.confirm": "\xBFInstalar Voz en vivo {version} en este perfil de DSH? La descarga requiere red. Reinicia DSH despu\xE9s si se solicita; nada se reinicia autom\xE1ticamente.",
|
|
2131
2133
|
"dsh-live-voice.commons.update.current": "Esta versi\xF3n ya est\xE1 instalada. Reinicia DSH para usarla.",
|
|
2134
|
+
"dsh-live-voice.commons.update.delayed": "La versi\xF3n solicitada a\xFAn no est\xE1 disponible para este perfil. Puede estar public\xE1ndose o esperando el minimumReleaseAge de pnpm. Espera y consulta el gestor de plugins de DSH; no se omiti\xF3 ninguna pol\xEDtica de seguridad.",
|
|
2132
2135
|
"dsh-live-voice.commons.update.done": "Voz en vivo {version} instalado. Recarga la p\xE1gina cuando quieras.",
|
|
2133
2136
|
"dsh-live-voice.commons.update.failed": "La actualizaci\xF3n fall\xF3. Puede que la versi\xF3n a\xFAn no est\xE9 en npm. Revisa el gestor de plugins de DSH.",
|
|
2137
|
+
"dsh-live-voice.commons.update.incompatible": "Esta versi\xF3n es incompatible con el DSH en ejecuci\xF3n. Consulta el gestor de plugins y usa versiones compatibles de DSH y del plugin; no se concedi\xF3 ninguna excepci\xF3n.",
|
|
2134
2138
|
"dsh-live-voice.commons.update.installing": "Actualizando\u2026",
|
|
2135
2139
|
"dsh-live-voice.commons.update.label": "Actualizaci\xF3n disponible",
|
|
2136
2140
|
"dsh-live-voice.commons.update.link": "Actualizaci\xF3n disponible: {version}. Abrir la p\xE1gina de esta versi\xF3n",
|
|
@@ -2524,8 +2528,10 @@ var fr = {
|
|
|
2524
2528
|
"dsh-live-voice.commons.update.cancel": "Annuler",
|
|
2525
2529
|
"dsh-live-voice.commons.update.confirm": "Installer Voix en direct {version} dans ce profil DSH ? Le t\xE9l\xE9chargement n\xE9cessite le r\xE9seau. Red\xE9marrez DSH ensuite si demand\xE9 ; rien ne red\xE9marre automatiquement.",
|
|
2526
2530
|
"dsh-live-voice.commons.update.current": "Cette version est d\xE9j\xE0 install\xE9e. Red\xE9marrez DSH pour l\u2019utiliser.",
|
|
2531
|
+
"dsh-live-voice.commons.update.delayed": "La version demand\xE9e n\u2019est pas encore disponible pour ce profil. La publication ou le minimumReleaseAge de pnpm peut la retarder. Patientez puis consultez le gestionnaire DSH ; aucune r\xE8gle de s\xE9curit\xE9 n\u2019a \xE9t\xE9 contourn\xE9e.",
|
|
2527
2532
|
"dsh-live-voice.commons.update.done": "Voix en direct {version} install\xE9. Rechargez la page quand vous le souhaitez.",
|
|
2528
2533
|
"dsh-live-voice.commons.update.failed": "\xC9chec de mise \xE0 jour. La version n\u2019est peut-\xEAtre pas encore sur npm. Consultez le gestionnaire de plugins DSH.",
|
|
2534
|
+
"dsh-live-voice.commons.update.incompatible": "Cette version est incompatible avec le DSH en cours. Consultez le gestionnaire de plugins et utilisez des versions compatibles de DSH et du plugin ; aucune d\xE9rogation n\u2019a \xE9t\xE9 accord\xE9e.",
|
|
2529
2535
|
"dsh-live-voice.commons.update.installing": "Mise \xE0 jour\u2026",
|
|
2530
2536
|
"dsh-live-voice.commons.update.label": "Mise \xE0 jour disponible",
|
|
2531
2537
|
"dsh-live-voice.commons.update.link": "Mise \xE0 jour disponible : {version}. Ouvrir la page de cette version",
|
|
@@ -2919,8 +2925,10 @@ var hi = {
|
|
|
2919
2925
|
"dsh-live-voice.commons.update.cancel": "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
|
|
2920
2926
|
"dsh-live-voice.commons.update.confirm": "\u0907\u0938 DSH \u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932 \u092E\u0947\u0902 \u0932\u093E\u0907\u0935 \u0935\u0949\u0907\u0938 {version} \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0915\u0930\u0947\u0902? \u0921\u093E\u0909\u0928\u0932\u094B\u0921 \u0915\u0947 \u0932\u093F\u090F \u0928\u0947\u091F\u0935\u0930\u094D\u0915 \u091A\u093E\u0939\u093F\u090F\u0964 \u0905\u0928\u0941\u0930\u094B\u0927 \u0939\u094B\u0928\u0947 \u092A\u0930 \u092C\u093E\u0926 \u092E\u0947\u0902 DSH \u092A\u0941\u0928\u0903 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902; \u0915\u0941\u091B \u092D\u0940 \u0905\u092A\u0928\u0947 \u0906\u092A \u092A\u0941\u0928\u0903 \u0936\u0941\u0930\u0942 \u0928\u0939\u0940\u0902 \u0939\u094B\u0917\u093E\u0964",
|
|
2921
2927
|
"dsh-live-voice.commons.update.current": "\u092F\u0939 \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u092A\u0939\u0932\u0947 \u0938\u0947 \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0939\u0948\u0964 \u0909\u092A\u092F\u094B\u0917 \u0915\u0947 \u0932\u093F\u090F DSH \u092A\u0941\u0928\u0903 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902\u0964",
|
|
2928
|
+
"dsh-live-voice.commons.update.delayed": "\u092E\u093E\u0901\u0917\u093E \u0917\u092F\u093E \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u0905\u092D\u0940 \u0907\u0938 \u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932 \u0915\u0947 \u0932\u093F\u090F \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 \u092A\u094D\u0930\u0915\u093E\u0936\u0928 \u091C\u093E\u0930\u0940 \u0939\u094B \u0938\u0915\u0924\u093E \u0939\u0948 \u092F\u093E pnpm \u0915\u093E minimumReleaseAge \u0935\u093F\u0932\u0902\u092C \u0915\u0930 \u0938\u0915\u0924\u093E \u0939\u0948\u0964 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0915\u0930\u0947\u0902, \u092B\u093F\u0930 DSH \u092A\u094D\u0932\u0917\u0907\u0928 \u092A\u094D\u0930\u092C\u0902\u0927\u0915 \u0926\u0947\u0916\u0947\u0902; \u0915\u094B\u0908 \u0938\u0941\u0930\u0915\u094D\u0937\u093E \u0928\u0940\u0924\u093F \u0928\u0939\u0940\u0902 \u092C\u0926\u0932\u0940 \u0917\u0908\u0964",
|
|
2922
2929
|
"dsh-live-voice.commons.update.done": "\u0932\u093E\u0907\u0935 \u0935\u0949\u0907\u0938 {version} \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0939\u094B \u0917\u092F\u093E\u0964 \u0924\u0948\u092F\u093E\u0930 \u0939\u094B\u0928\u0947 \u092A\u0930 \u092A\u0947\u091C \u0930\u0940\u0932\u094B\u0921 \u0915\u0930\u0947\u0902\u0964",
|
|
2923
2930
|
"dsh-live-voice.commons.update.failed": "\u0905\u092A\u0921\u0947\u091F \u0935\u093F\u092B\u0932\u0964 \u0930\u093F\u0932\u0940\u091C\u093C \u0905\u092D\u0940 npm \u092A\u0930 \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u0924\u0940\u0964 DSH \u092A\u094D\u0932\u0917\u0907\u0928 \u092A\u094D\u0930\u092C\u0902\u0927\u0915 \u0926\u0947\u0916\u0947\u0902\u0964",
|
|
2931
|
+
"dsh-live-voice.commons.update.incompatible": "\u092F\u0939 \u0930\u093F\u0932\u0940\u091C\u093C \u091A\u0932 \u0930\u0939\u0947 DSH \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u0915\u0947 \u0938\u093E\u0925 \u0938\u0902\u0917\u0924 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 DSH \u092A\u094D\u0932\u0917\u0907\u0928 \u092A\u094D\u0930\u092C\u0902\u0927\u0915 \u0926\u0947\u0916\u0947\u0902 \u0914\u0930 \u0938\u0902\u0917\u0924 DSH \u0924\u0925\u093E \u092A\u094D\u0932\u0917\u0907\u0928 \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u0909\u092A\u092F\u094B\u0917 \u0915\u0930\u0947\u0902; \u0915\u094B\u0908 \u0938\u0902\u0917\u0924\u0924\u093E \u091B\u0942\u091F \u0928\u0939\u0940\u0902 \u0926\u0940 \u0917\u0908\u0964",
|
|
2924
2932
|
"dsh-live-voice.commons.update.installing": "\u0905\u092A\u0921\u0947\u091F \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
|
|
2925
2933
|
"dsh-live-voice.commons.update.label": "\u0905\u092A\u0921\u0947\u091F \u0909\u092A\u0932\u092C\u094D\u0927 \u0939\u0948",
|
|
2926
2934
|
"dsh-live-voice.commons.update.link": "\u0905\u092A\u0921\u0947\u091F \u0909\u092A\u0932\u092C\u094D\u0927 \u0939\u0948: {version}\u0964 \u0930\u093F\u0932\u0940\u091C\u093C \u0916\u094B\u0932\u0947\u0902",
|
|
@@ -3314,8 +3322,10 @@ var ptBR = {
|
|
|
3314
3322
|
"dsh-live-voice.commons.update.cancel": "Cancelar",
|
|
3315
3323
|
"dsh-live-voice.commons.update.confirm": "Instalar Voz ao Vivo {version} neste perfil do DSH? O download precisa de rede. Reinicie o DSH depois, se solicitado; nada reinicia automaticamente.",
|
|
3316
3324
|
"dsh-live-voice.commons.update.current": "Esta vers\xE3o j\xE1 est\xE1 instalada. Reinicie o DSH para utiliz\xE1-la.",
|
|
3325
|
+
"dsh-live-voice.commons.update.delayed": "A vers\xE3o solicitada ainda n\xE3o est\xE1 dispon\xEDvel para este perfil. A publica\xE7\xE3o pode estar em andamento ou aguardando o minimumReleaseAge do pnpm. Aguarde e consulte o gerenciador de plugins do DSH; nenhuma pol\xEDtica de seguran\xE7a foi ignorada.",
|
|
3317
3326
|
"dsh-live-voice.commons.update.done": "Voz ao Vivo {version} instalado. Recarregue a p\xE1gina quando quiser.",
|
|
3318
3327
|
"dsh-live-voice.commons.update.failed": "Falha na atualiza\xE7\xE3o. A vers\xE3o pode ainda n\xE3o estar no npm. Verifique o gerenciador de plugins do DSH.",
|
|
3328
|
+
"dsh-live-voice.commons.update.incompatible": "Esta vers\xE3o \xE9 incompat\xEDvel com o DSH em execu\xE7\xE3o. Consulte o gerenciador de plugins e use vers\xF5es compat\xEDveis do DSH e do plugin; nenhuma exce\xE7\xE3o de compatibilidade foi concedida.",
|
|
3319
3329
|
"dsh-live-voice.commons.update.installing": "Atualizando\u2026",
|
|
3320
3330
|
"dsh-live-voice.commons.update.label": "Atualiza\xE7\xE3o dispon\xEDvel",
|
|
3321
3331
|
"dsh-live-voice.commons.update.link": "Atualiza\xE7\xE3o dispon\xEDvel: {version}. Abrir lan\xE7amento",
|
|
@@ -3709,8 +3719,10 @@ var zh = {
|
|
|
3709
3719
|
"dsh-live-voice.commons.update.cancel": "\u53D6\u6D88",
|
|
3710
3720
|
"dsh-live-voice.commons.update.confirm": "\u5728\u6B64 DSH \u914D\u7F6E\u4E2D\u5B89\u88C5 \u5B9E\u65F6\u8BED\u97F3 {version}\uFF1F\u4E0B\u8F7D\u9700\u8981\u7F51\u7EDC\u3002\u82E5\u63D0\u793A\uFF0C\u8BF7\u4E4B\u540E\u91CD\u542F DSH\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u91CD\u542F\u3002",
|
|
3711
3721
|
"dsh-live-voice.commons.update.current": "\u6B64\u7248\u672C\u5DF2\u5B89\u88C5\u3002\u8BF7\u91CD\u542F DSH \u540E\u4F7F\u7528\u3002",
|
|
3722
|
+
"dsh-live-voice.commons.update.delayed": "\u6B64\u914D\u7F6E\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u6240\u8BF7\u6C42\u7684\u7248\u672C\u3002\u5B83\u53EF\u80FD\u4ECD\u5728\u53D1\u5E03\uFF0C\u6216\u53D7 pnpm minimumReleaseAge \u9650\u5236\u3002\u8BF7\u7A0D\u540E\u68C0\u67E5 DSH \u63D2\u4EF6\u7BA1\u7406\u5668\uFF1B\u672A\u7ED5\u8FC7\u4EFB\u4F55\u5B89\u5168\u7B56\u7565\u3002",
|
|
3712
3723
|
"dsh-live-voice.commons.update.done": "\u5B9E\u65F6\u8BED\u97F3 {version} \u5DF2\u5B89\u88C5\u3002\u51C6\u5907\u597D\u540E\u8BF7\u5237\u65B0\u9875\u9762\u3002",
|
|
3713
3724
|
"dsh-live-voice.commons.update.failed": "\u66F4\u65B0\u5931\u8D25\u3002\u8BE5\u7248\u672C\u53EF\u80FD\u5C1A\u672A\u53D1\u5E03\u5230 npm\u3002\u8BF7\u67E5\u770B DSH \u63D2\u4EF6\u7BA1\u7406\u5668\u3002",
|
|
3725
|
+
"dsh-live-voice.commons.update.incompatible": "\u6B64\u7248\u672C\u4E0E\u6B63\u5728\u8FD0\u884C\u7684 DSH \u4E0D\u517C\u5BB9\u3002\u8BF7\u68C0\u67E5 DSH \u63D2\u4EF6\u7BA1\u7406\u5668\u5E76\u4F7F\u7528\u517C\u5BB9\u7684 DSH \u548C\u63D2\u4EF6\u7248\u672C\uFF1B\u672A\u6388\u4E88\u517C\u5BB9\u6027\u8C41\u514D\u3002",
|
|
3714
3726
|
"dsh-live-voice.commons.update.installing": "\u6B63\u5728\u66F4\u65B0\u2026",
|
|
3715
3727
|
"dsh-live-voice.commons.update.label": "\u6709\u53EF\u7528\u66F4\u65B0",
|
|
3716
3728
|
"dsh-live-voice.commons.update.link": "\u6709\u53EF\u7528\u66F4\u65B0\uFF1A{version}\u3002\u6253\u5F00\u53D1\u5E03\u9875\u9762",
|
|
@@ -6896,6 +6908,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
6896
6908
|
recognizing: false,
|
|
6897
6909
|
pendingTranscriptions: 0,
|
|
6898
6910
|
pendingTranscriptionRequests: void 0,
|
|
6911
|
+
pendingDeliveryTranscriptions: void 0,
|
|
6899
6912
|
muted: normalizeSettings(settings).microphoneEnabled === false,
|
|
6900
6913
|
speaking: false,
|
|
6901
6914
|
paused: false,
|
|
@@ -7240,14 +7253,20 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7240
7253
|
onResult: (result) => {
|
|
7241
7254
|
if (valid()) this.onResult(result);
|
|
7242
7255
|
},
|
|
7243
|
-
onProcessingChange: ({ pending = 0, transcribing } = {}) => {
|
|
7256
|
+
onProcessingChange: ({ pending = 0, transcribing, deliveryPending } = {}) => {
|
|
7244
7257
|
if (!valid()) return;
|
|
7245
7258
|
const count = Number.isSafeInteger(pending) && pending >= 0 ? pending : 0;
|
|
7246
7259
|
const requests = Number.isSafeInteger(transcribing) && transcribing >= 0 ? transcribing : count;
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7260
|
+
const delivery = Number.isSafeInteger(deliveryPending) && deliveryPending >= 0 ? deliveryPending : count;
|
|
7261
|
+
this.patch({
|
|
7262
|
+
pendingTranscriptions: count,
|
|
7263
|
+
pendingTranscriptionRequests: requests,
|
|
7264
|
+
pendingDeliveryTranscriptions: delivery
|
|
7265
|
+
});
|
|
7266
|
+
if (delivery > 0) this.cancelAutoSend({ preserveIntent: true });
|
|
7267
|
+
else if (this.holdToTalkRelease) {
|
|
7268
|
+
if (count === 0) this._finishHoldToTalk();
|
|
7269
|
+
} else this.maybeScheduleAutoSend();
|
|
7251
7270
|
if (count === 0) this._drain();
|
|
7252
7271
|
},
|
|
7253
7272
|
onActivity: (active) => {
|
|
@@ -7448,15 +7467,18 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7448
7467
|
this._handleSpeechInterruption(this._speechActivity());
|
|
7449
7468
|
this._drain();
|
|
7450
7469
|
}
|
|
7470
|
+
deliveryPending() {
|
|
7471
|
+
return this.snapshot.pendingDeliveryTranscriptions ?? this.snapshot.pendingTranscriptions;
|
|
7472
|
+
}
|
|
7451
7473
|
maybeScheduleAutoSend(draft = this.autoSendIntent) {
|
|
7452
7474
|
if (draft == null || draft !== this.composer.getDraft()) return;
|
|
7453
7475
|
if (this.autoSendTimer !== null) return;
|
|
7454
|
-
if (this.snapshot.settings.sendingMode === "manual" || this.snapshot.muted || this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.
|
|
7476
|
+
if (this.snapshot.settings.sendingMode === "manual" || this.snapshot.muted || this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.deliveryPending() > 0)
|
|
7455
7477
|
return;
|
|
7456
7478
|
this.scheduleAutoSend(draft);
|
|
7457
7479
|
}
|
|
7458
7480
|
refreshDeliveryReadiness() {
|
|
7459
|
-
if (this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.
|
|
7481
|
+
if (this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.deliveryPending() > 0)
|
|
7460
7482
|
this.cancelAutoSend({ preserveIntent: true });
|
|
7461
7483
|
else this.maybeScheduleAutoSend();
|
|
7462
7484
|
}
|
|
@@ -7513,6 +7535,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7513
7535
|
recognizing: false,
|
|
7514
7536
|
pendingTranscriptions: 0,
|
|
7515
7537
|
pendingTranscriptionRequests: 0,
|
|
7538
|
+
pendingDeliveryTranscriptions: 0,
|
|
7516
7539
|
starting: false
|
|
7517
7540
|
});
|
|
7518
7541
|
return this._input(async () => {
|
|
@@ -8089,6 +8112,8 @@ var MeetingController = class {
|
|
|
8089
8112
|
if (Object.hasOwn(next, "pending")) policy.pendingTranscriptions = next.pending;
|
|
8090
8113
|
if (Object.hasOwn(next, "transcribing"))
|
|
8091
8114
|
policy.pendingTranscriptionRequests = next.transcribing;
|
|
8115
|
+
if (Object.hasOwn(next, "deliveryPending"))
|
|
8116
|
+
policy.pendingDeliveryTranscriptions = next.deliveryPending;
|
|
8092
8117
|
input?.patch(policy);
|
|
8093
8118
|
}
|
|
8094
8119
|
this.snapshot = {
|
|
@@ -8261,11 +8286,12 @@ var MeetingController = class {
|
|
|
8261
8286
|
this.onActivity?.(source, active);
|
|
8262
8287
|
input.refreshDeliveryReadiness();
|
|
8263
8288
|
},
|
|
8264
|
-
onProcessingChange: ({ pending, transcribing }) => {
|
|
8289
|
+
onProcessingChange: ({ pending, transcribing, deliveryPending }) => {
|
|
8265
8290
|
if (!valid()) return;
|
|
8266
8291
|
const requests = Number.isSafeInteger(transcribing) && transcribing >= 0 ? transcribing : pending;
|
|
8267
|
-
|
|
8268
|
-
|
|
8292
|
+
const delivery = Number.isSafeInteger(deliveryPending) && deliveryPending >= 0 ? deliveryPending : pending;
|
|
8293
|
+
this.patch(source, { pending, transcribing: requests, deliveryPending: delivery });
|
|
8294
|
+
if (delivery) input.cancelAutoSend({ preserveIntent: true });
|
|
8269
8295
|
else if (!input.getSnapshot().muted) input.maybeScheduleAutoSend();
|
|
8270
8296
|
},
|
|
8271
8297
|
onError: (error) => {
|
|
@@ -8308,12 +8334,19 @@ var MeetingController = class {
|
|
|
8308
8334
|
listening: false,
|
|
8309
8335
|
recognizing: false,
|
|
8310
8336
|
pendingTranscriptions: 0,
|
|
8311
|
-
pendingTranscriptionRequests: 0
|
|
8337
|
+
pendingTranscriptionRequests: 0,
|
|
8338
|
+
pendingDeliveryTranscriptions: 0
|
|
8312
8339
|
});
|
|
8313
8340
|
this.onActivity?.(source, false);
|
|
8314
8341
|
job?.abort.abort();
|
|
8315
8342
|
this.transcript.setActive(source, false);
|
|
8316
|
-
this.patch(source, {
|
|
8343
|
+
this.patch(source, {
|
|
8344
|
+
starting: false,
|
|
8345
|
+
listening: false,
|
|
8346
|
+
pending: 0,
|
|
8347
|
+
transcribing: 0,
|
|
8348
|
+
deliveryPending: 0
|
|
8349
|
+
});
|
|
8317
8350
|
await job?.engine?.stop();
|
|
8318
8351
|
await job?.meter?.stop();
|
|
8319
8352
|
}
|
|
@@ -9562,6 +9595,7 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9562
9595
|
onProcessingChange,
|
|
9563
9596
|
tail: Promise.resolve(),
|
|
9564
9597
|
pendingWrites: 0,
|
|
9598
|
+
pendingSpeechWrites: 0,
|
|
9565
9599
|
lang,
|
|
9566
9600
|
operation: uid(),
|
|
9567
9601
|
capturedSamples: 0,
|
|
@@ -9584,7 +9618,9 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9584
9618
|
active: !!session.activeRequest,
|
|
9585
9619
|
// Only queued/running speech attempts belong in the visible transcription status.
|
|
9586
9620
|
transcribing: session.transcriptionQueue.length + (session.activeRequest ? 1 : 0),
|
|
9587
|
-
//
|
|
9621
|
+
// Speech-boundary bookkeeping must settle before delivery, but silent PCM writes must not reset it.
|
|
9622
|
+
deliveryPending: session.transcriptionQueue.length + (session.activeRequest ? 1 : 0) + session.pendingSpeechWrites + (session.finishing ? 1 : 0),
|
|
9623
|
+
// All writes still protect release, playback admission and page leave.
|
|
9588
9624
|
pending: session.transcriptionQueue.length + (session.activeRequest ? 1 : 0) + session.pendingWrites + (session.finishing ? 1 : 0)
|
|
9589
9625
|
});
|
|
9590
9626
|
};
|
|
@@ -9594,14 +9630,16 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9594
9630
|
onError?.(new Error(engine.recordingErrorMessage(error)));
|
|
9595
9631
|
void engine.stop();
|
|
9596
9632
|
};
|
|
9597
|
-
const serialize = (task, preserve = false) => {
|
|
9633
|
+
const serialize = (task, preserve = false, speech = false) => {
|
|
9598
9634
|
session.pendingWrites++;
|
|
9635
|
+
if (speech) session.pendingSpeechWrites++;
|
|
9599
9636
|
notify2();
|
|
9600
9637
|
const work = session.tail.then(async () => {
|
|
9601
9638
|
if (preserve || valid()) await task();
|
|
9602
9639
|
});
|
|
9603
9640
|
session.tail = work.catch(failure).finally(() => {
|
|
9604
9641
|
session.pendingWrites--;
|
|
9642
|
+
if (speech) session.pendingSpeechWrites--;
|
|
9605
9643
|
notify2();
|
|
9606
9644
|
});
|
|
9607
9645
|
return work;
|
|
@@ -9621,10 +9659,14 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9621
9659
|
if (through - Math.max(liveSpeechStart, lastSplitQueued) < max) return;
|
|
9622
9660
|
const end = Math.max(liveSpeechStart, lastSplitQueued) + max;
|
|
9623
9661
|
lastSplitQueued = end;
|
|
9624
|
-
void serialize(
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9662
|
+
void serialize(
|
|
9663
|
+
async () => {
|
|
9664
|
+
const speech = [...writer.session.pins].reverse().find((p) => p.kind === "speech-start");
|
|
9665
|
+
if (speech && valid()) await queueSpeech(speech, end);
|
|
9666
|
+
},
|
|
9667
|
+
false,
|
|
9668
|
+
true
|
|
9669
|
+
).catch(() => {
|
|
9628
9670
|
});
|
|
9629
9671
|
};
|
|
9630
9672
|
const queueSpeech = async (pin2, end) => {
|
|
@@ -9665,14 +9707,18 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9665
9707
|
liveSpeechStart = startSample;
|
|
9666
9708
|
lastSplitQueued = startSample;
|
|
9667
9709
|
} else liveSpeechStart = null;
|
|
9668
|
-
void serialize(
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9710
|
+
void serialize(
|
|
9711
|
+
async () => {
|
|
9712
|
+
const previous = writer.session.pins.at(-1);
|
|
9713
|
+
if (kind === "speech-start" && previous?.kind === "silence-start" && previous.atSample > 0 && startSample - previous.atSample >= paragraphSamples)
|
|
9714
|
+
paragraphStarts.add(startSample);
|
|
9715
|
+
await writer.pin(kind, startSample, confirmedSample);
|
|
9716
|
+
if (valid() && kind === "silence-start" && previous?.kind === "speech-start")
|
|
9717
|
+
await queueSpeech(previous, startSample);
|
|
9718
|
+
},
|
|
9719
|
+
true,
|
|
9720
|
+
true
|
|
9721
|
+
).catch(() => {
|
|
9676
9722
|
});
|
|
9677
9723
|
};
|
|
9678
9724
|
const detector = new PinDetector({
|
|
@@ -10053,7 +10099,13 @@ var WhisperHttpRecognitionEngine = class {
|
|
|
10053
10099
|
this.session = null;
|
|
10054
10100
|
session.signal?.removeEventListener("abort", session.abort);
|
|
10055
10101
|
await session.stopRecording();
|
|
10056
|
-
session.onProcessingChange?.({
|
|
10102
|
+
session.onProcessingChange?.({
|
|
10103
|
+
queued: 0,
|
|
10104
|
+
active: false,
|
|
10105
|
+
transcribing: 0,
|
|
10106
|
+
deliveryPending: 0,
|
|
10107
|
+
pending: 0
|
|
10108
|
+
});
|
|
10057
10109
|
}
|
|
10058
10110
|
};
|
|
10059
10111
|
|
|
@@ -10532,8 +10584,8 @@ function createDeviceOptions(devices, kind, systemDefault, fallback) {
|
|
|
10532
10584
|
var import_react36 = __toESM(require("react"), 1);
|
|
10533
10585
|
|
|
10534
10586
|
// src/modules/settings/services/releases.ts
|
|
10535
|
-
var CURRENT_VERSION = "0.6.
|
|
10536
|
-
var TESTED_DSH_VERSION = "0.2.1-alpha.
|
|
10587
|
+
var CURRENT_VERSION = "0.6.1";
|
|
10588
|
+
var TESTED_DSH_VERSION = "0.2.1-alpha.3";
|
|
10537
10589
|
var REPOSITORY_URL = "https://github.com/victorwads/dsh-live-voice";
|
|
10538
10590
|
var RELEASES_URL = `${REPOSITORY_URL}/releases`;
|
|
10539
10591
|
var LATEST_RELEASE_API_URL = "https://api.github.com/repos/victorwads/dsh-live-voice/releases/latest";
|
|
@@ -13057,7 +13109,9 @@ function createPluginUpdate({
|
|
|
13057
13109
|
if (!result?.ok) return patch({ ...state, phase: "uncertain", problem: "uncertain" });
|
|
13058
13110
|
const value = result.value;
|
|
13059
13111
|
if (value.application === "failed" || value.application === "cancelled")
|
|
13060
|
-
return fail(
|
|
13112
|
+
return fail(
|
|
13113
|
+
value.error?.code === "incompatible-version" || value.packageResult?.incompatible?.length ? "incompatible" : value.pendingBuilds?.length || value.packageResult?.kind === "build-blocked" ? "scripts" : ["not-found", "no-matching-version"].includes(value.packageResult?.kind) ? "delayed" : "failed"
|
|
13114
|
+
);
|
|
13061
13115
|
if (value.bundle !== "dsh-live-voice" || value.version !== state.version || !["applied", "restart-required"].includes(value.application))
|
|
13062
13116
|
return patch({ ...state, phase: "uncertain", problem: "uncertain" });
|
|
13063
13117
|
patch({ ...state, phase: value.application === "restart-required" ? "restart" : "done" });
|
|
@@ -13270,7 +13324,7 @@ async function removeBrowserModel(key, storage = caches) {
|
|
|
13270
13324
|
}
|
|
13271
13325
|
|
|
13272
13326
|
// src/modules/recognition/local/assets.ts
|
|
13273
|
-
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.6.
|
|
13327
|
+
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.6.1" : "test") + "/";
|
|
13274
13328
|
|
|
13275
13329
|
// src/modules/recognition/local/runtimeIdentity.ts
|
|
13276
13330
|
var workerProtocol = "asr-worker-v3-ort-1.31.0-dev.20260914-8d85527a0";
|
|
@@ -14008,7 +14062,7 @@ function apply(ctx) {
|
|
|
14008
14062
|
},
|
|
14009
14063
|
updatePlaybackSettings: (next) => meetingEntry?.controller.updateSettings(next),
|
|
14010
14064
|
handleQuestionResult: (result) => meetingEntry?.controller.composer.handleQuestionResult?.(result),
|
|
14011
|
-
canAutoSend: () => !!meetingEntry && !meetingEntry.controller.getSnapshot().recognizing && !meetingEntry.controller.
|
|
14065
|
+
canAutoSend: () => !!meetingEntry && !meetingEntry.controller.getSnapshot().recognizing && !meetingEntry.controller.deliveryPending()
|
|
14012
14066
|
},
|
|
14013
14067
|
settings: () => preferences.getSnapshot(),
|
|
14014
14068
|
savePreferences: (controller, next) => savePreferences(controller, next),
|
|
@@ -14367,7 +14421,7 @@ function apply(ctx) {
|
|
|
14367
14421
|
meter,
|
|
14368
14422
|
composer: {
|
|
14369
14423
|
getDraft: () => entry.draft,
|
|
14370
|
-
canAutoSend: () => !meeting.
|
|
14424
|
+
canAutoSend: () => !meeting.input().deliveryPending() && !meeting.input().getSnapshot().recognizing,
|
|
14371
14425
|
appendFinal: (text, startedAt, paragraphBefore) => {
|
|
14372
14426
|
meeting.cancelDelivery();
|
|
14373
14427
|
if (meetingEntry === entry && (entry.meeting?.getSnapshot().shared.listening || entry.controller.getSnapshot().timestamps)) {
|
package/lib/server.js
CHANGED
|
@@ -996,7 +996,7 @@ var VAD_ASSET_TYPES = {
|
|
|
996
996
|
};
|
|
997
997
|
var MAX_VAD_ASSET_BYTES = 32 * 1024 * 1024;
|
|
998
998
|
function registerVadAssetRoutes(ctx, options = {}) {
|
|
999
|
-
const version = options.version ?? (true ? "0.6.
|
|
999
|
+
const version = options.version ?? (true ? "0.6.1" : "test");
|
|
1000
1000
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9.+-]{0,127}$/.test(version))
|
|
1001
1001
|
throw new Error("Invalid VAD asset version");
|
|
1002
1002
|
const directory = options.directory ?? fileURLToPath(new URL("./vad/", import.meta.url));
|
|
@@ -1090,7 +1090,7 @@ import { lstat as lstat2, readFile as readFile5 } from "node:fs/promises";
|
|
|
1090
1090
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1091
1091
|
import { join as join7 } from "node:path";
|
|
1092
1092
|
function registerBrowserAsrAssets(ctx, options = {}) {
|
|
1093
|
-
const version = options.version ?? (true ? "0.6.
|
|
1093
|
+
const version = options.version ?? (true ? "0.6.1" : "test");
|
|
1094
1094
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9.+-]{0,127}$/.test(version)) throw new Error("invalid-version");
|
|
1095
1095
|
const base = "/api/dsh-live-voice/asr/" + encodeURIComponent(version) + "/";
|
|
1096
1096
|
const directory = options.directory ?? fileURLToPath2(new URL("./asr/", import.meta.url));
|
package/lib/vad/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"pluginVersion": "0.6.
|
|
3
|
+
"pluginVersion": "0.6.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@ricky0123/vad-web": "0.0.31",
|
|
6
6
|
"onnxruntime-web": "1.22.0"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"vad.worker.js": {
|
|
43
43
|
"bytes": 79337,
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "8408341e3f7ea6773036bed3d002fcb0a037af3e6cd2a419477eb67a8e18fde4"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
package/lib/vad/vad.worker.js
CHANGED
|
@@ -1632,7 +1632,7 @@ var vo = Ke;
|
|
|
1632
1632
|
Object.defineProperty(I.versions, "web", { value: $t, enumerable: true });
|
|
1633
1633
|
|
|
1634
1634
|
// src/modules/recognition/vad/assets.ts
|
|
1635
|
-
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.6.
|
|
1635
|
+
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.6.1" : "test") + "/";
|
|
1636
1636
|
|
|
1637
1637
|
// src/modules/recognition/vad/StreamingResampler.ts
|
|
1638
1638
|
var StreamingResampler = class {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-live-voice",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"dshTestedVersion": "0.2.1-alpha.
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"dshTestedVersion": "0.2.1-alpha.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "node --experimental-strip-types ../../scripts/build.ts --package live-voice",
|
|
@@ -92,12 +92,14 @@
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
"dependencies": {
|
|
95
|
+
"@wads.dev/i18n-react": "^0.0.1-alpha.0",
|
|
96
|
+
"@wads.dev/i18n-ts": "0.0.1-alpha.0",
|
|
97
|
+
"marked": "^18.1.0"
|
|
98
|
+
},
|
|
99
|
+
"devDependencies": {
|
|
95
100
|
"@dlv/ort-local": "npm:onnxruntime-web@1.31.0-dev.20260914-8d85527a0",
|
|
96
101
|
"@huggingface/transformers": "4.3.1",
|
|
97
102
|
"@ricky0123/vad-web": "0.0.31",
|
|
98
|
-
"@wads.dev/i18n-react": "^0.0.1-alpha.0",
|
|
99
|
-
"@wads.dev/i18n-ts": "0.0.1-alpha.0",
|
|
100
|
-
"marked": "^18.1.0",
|
|
101
103
|
"onnxruntime-web": "1.22.0"
|
|
102
104
|
}
|
|
103
105
|
}
|
package/src/app/client/apply.tsx
CHANGED
|
@@ -106,7 +106,7 @@ export function apply(ctx) {
|
|
|
106
106
|
canAutoSend: () =>
|
|
107
107
|
!!meetingEntry &&
|
|
108
108
|
!meetingEntry.controller.getSnapshot().recognizing &&
|
|
109
|
-
!meetingEntry.controller.
|
|
109
|
+
!meetingEntry.controller.deliveryPending(),
|
|
110
110
|
},
|
|
111
111
|
settings: () => preferences.getSnapshot(),
|
|
112
112
|
savePreferences: (controller, next) => savePreferences(controller, next),
|
|
@@ -521,7 +521,7 @@ export function apply(ctx) {
|
|
|
521
521
|
composer: {
|
|
522
522
|
getDraft: () => entry.draft,
|
|
523
523
|
canAutoSend: () =>
|
|
524
|
-
!meeting.
|
|
524
|
+
!meeting.input().deliveryPending() && !meeting.input().getSnapshot().recognizing,
|
|
525
525
|
appendFinal: (text, startedAt, paragraphBefore) => {
|
|
526
526
|
meeting.cancelDelivery();
|
|
527
527
|
if (
|
|
@@ -41,8 +41,10 @@ export interface LiveVoiceTranslation extends Translation {
|
|
|
41
41
|
'dsh-live-voice.commons.update.cancel': string;
|
|
42
42
|
'dsh-live-voice.commons.update.confirm': string;
|
|
43
43
|
'dsh-live-voice.commons.update.current': string;
|
|
44
|
+
'dsh-live-voice.commons.update.delayed': string;
|
|
44
45
|
'dsh-live-voice.commons.update.done': string;
|
|
45
46
|
'dsh-live-voice.commons.update.failed': string;
|
|
47
|
+
'dsh-live-voice.commons.update.incompatible': string;
|
|
46
48
|
'dsh-live-voice.commons.update.installing': string;
|
|
47
49
|
'dsh-live-voice.commons.update.label': string;
|
|
48
50
|
'dsh-live-voice.commons.update.link': string;
|
|
@@ -43,10 +43,14 @@ const en: LiveVoiceTranslation = {
|
|
|
43
43
|
'Install Live Voice {version} in this DSH profile? Download requires network access. Restart DSH afterward when requested; nothing restarts automatically.',
|
|
44
44
|
'dsh-live-voice.commons.update.current':
|
|
45
45
|
'This version is already installed. Restart DSH to use it.',
|
|
46
|
+
'dsh-live-voice.commons.update.delayed':
|
|
47
|
+
'The requested version is not available to this profile yet. It may still be publishing or be delayed by pnpm minimumReleaseAge. Wait, then check the DSH plugin manager; no security policy was bypassed.',
|
|
46
48
|
'dsh-live-voice.commons.update.done':
|
|
47
49
|
'Live Voice {version} installed. Reload the page when ready.',
|
|
48
50
|
'dsh-live-voice.commons.update.failed':
|
|
49
51
|
'Update failed. The release may not be published on npm yet. Check the DSH plugin manager.',
|
|
52
|
+
'dsh-live-voice.commons.update.incompatible':
|
|
53
|
+
'This release is incompatible with the running DSH version. Check the DSH plugin manager and use a compatible DSH/plugin version; no compatibility exemption was granted.',
|
|
50
54
|
'dsh-live-voice.commons.update.installing': 'Updating…',
|
|
51
55
|
'dsh-live-voice.commons.update.label': 'Update available',
|
|
52
56
|
'dsh-live-voice.commons.update.link': 'Update available: {version}. Open release',
|
|
@@ -44,10 +44,14 @@ const es: LiveVoiceTranslation = {
|
|
|
44
44
|
'¿Instalar Voz en vivo {version} en este perfil de DSH? La descarga requiere red. Reinicia DSH después si se solicita; nada se reinicia automáticamente.',
|
|
45
45
|
'dsh-live-voice.commons.update.current':
|
|
46
46
|
'Esta versión ya está instalada. Reinicia DSH para usarla.',
|
|
47
|
+
'dsh-live-voice.commons.update.delayed':
|
|
48
|
+
'La versión solicitada aún no está disponible para este perfil. Puede estar publicándose o esperando el minimumReleaseAge de pnpm. Espera y consulta el gestor de plugins de DSH; no se omitió ninguna política de seguridad.',
|
|
47
49
|
'dsh-live-voice.commons.update.done':
|
|
48
50
|
'Voz en vivo {version} instalado. Recarga la página cuando quieras.',
|
|
49
51
|
'dsh-live-voice.commons.update.failed':
|
|
50
52
|
'La actualización falló. Puede que la versión aún no esté en npm. Revisa el gestor de plugins de DSH.',
|
|
53
|
+
'dsh-live-voice.commons.update.incompatible':
|
|
54
|
+
'Esta versión es incompatible con el DSH en ejecución. Consulta el gestor de plugins y usa versiones compatibles de DSH y del plugin; no se concedió ninguna excepción.',
|
|
51
55
|
'dsh-live-voice.commons.update.installing': 'Actualizando…',
|
|
52
56
|
'dsh-live-voice.commons.update.label': 'Actualización disponible',
|
|
53
57
|
'dsh-live-voice.commons.update.link':
|
|
@@ -45,10 +45,14 @@ const fr: LiveVoiceTranslation = {
|
|
|
45
45
|
'Installer Voix en direct {version} dans ce profil DSH ? Le téléchargement nécessite le réseau. Redémarrez DSH ensuite si demandé ; rien ne redémarre automatiquement.',
|
|
46
46
|
'dsh-live-voice.commons.update.current':
|
|
47
47
|
'Cette version est déjà installée. Redémarrez DSH pour l’utiliser.',
|
|
48
|
+
'dsh-live-voice.commons.update.delayed':
|
|
49
|
+
'La version demandée n’est pas encore disponible pour ce profil. La publication ou le minimumReleaseAge de pnpm peut la retarder. Patientez puis consultez le gestionnaire DSH ; aucune règle de sécurité n’a été contournée.',
|
|
48
50
|
'dsh-live-voice.commons.update.done':
|
|
49
51
|
'Voix en direct {version} installé. Rechargez la page quand vous le souhaitez.',
|
|
50
52
|
'dsh-live-voice.commons.update.failed':
|
|
51
53
|
'Échec de mise à jour. La version n’est peut-être pas encore sur npm. Consultez le gestionnaire de plugins DSH.',
|
|
54
|
+
'dsh-live-voice.commons.update.incompatible':
|
|
55
|
+
'Cette version est incompatible avec le DSH en cours. Consultez le gestionnaire de plugins et utilisez des versions compatibles de DSH et du plugin ; aucune dérogation n’a été accordée.',
|
|
52
56
|
'dsh-live-voice.commons.update.installing': 'Mise à jour…',
|
|
53
57
|
'dsh-live-voice.commons.update.label': 'Mise à jour disponible',
|
|
54
58
|
'dsh-live-voice.commons.update.link':
|
|
@@ -43,10 +43,14 @@ const hi: LiveVoiceTranslation = {
|
|
|
43
43
|
'इस DSH प्रोफ़ाइल में लाइव वॉइस {version} इंस्टॉल करें? डाउनलोड के लिए नेटवर्क चाहिए। अनुरोध होने पर बाद में DSH पुनः शुरू करें; कुछ भी अपने आप पुनः शुरू नहीं होगा।',
|
|
44
44
|
'dsh-live-voice.commons.update.current':
|
|
45
45
|
'यह संस्करण पहले से इंस्टॉल है। उपयोग के लिए DSH पुनः शुरू करें।',
|
|
46
|
+
'dsh-live-voice.commons.update.delayed':
|
|
47
|
+
'माँगा गया संस्करण अभी इस प्रोफ़ाइल के लिए उपलब्ध नहीं है। प्रकाशन जारी हो सकता है या pnpm का minimumReleaseAge विलंब कर सकता है। प्रतीक्षा करें, फिर DSH प्लगइन प्रबंधक देखें; कोई सुरक्षा नीति नहीं बदली गई।',
|
|
46
48
|
'dsh-live-voice.commons.update.done':
|
|
47
49
|
'लाइव वॉइस {version} इंस्टॉल हो गया। तैयार होने पर पेज रीलोड करें।',
|
|
48
50
|
'dsh-live-voice.commons.update.failed':
|
|
49
51
|
'अपडेट विफल। रिलीज़ अभी npm पर उपलब्ध नहीं हो सकती। DSH प्लगइन प्रबंधक देखें।',
|
|
52
|
+
'dsh-live-voice.commons.update.incompatible':
|
|
53
|
+
'यह रिलीज़ चल रहे DSH संस्करण के साथ संगत नहीं है। DSH प्लगइन प्रबंधक देखें और संगत DSH तथा प्लगइन संस्करण उपयोग करें; कोई संगतता छूट नहीं दी गई।',
|
|
50
54
|
'dsh-live-voice.commons.update.installing': 'अपडेट हो रहा है…',
|
|
51
55
|
'dsh-live-voice.commons.update.label': 'अपडेट उपलब्ध है',
|
|
52
56
|
'dsh-live-voice.commons.update.link': 'अपडेट उपलब्ध है: {version}। रिलीज़ खोलें',
|
|
@@ -44,10 +44,14 @@ const ptBR: LiveVoiceTranslation = {
|
|
|
44
44
|
'Instalar Voz ao Vivo {version} neste perfil do DSH? O download precisa de rede. Reinicie o DSH depois, se solicitado; nada reinicia automaticamente.',
|
|
45
45
|
'dsh-live-voice.commons.update.current':
|
|
46
46
|
'Esta versão já está instalada. Reinicie o DSH para utilizá-la.',
|
|
47
|
+
'dsh-live-voice.commons.update.delayed':
|
|
48
|
+
'A versão solicitada ainda não está disponível para este perfil. A publicação pode estar em andamento ou aguardando o minimumReleaseAge do pnpm. Aguarde e consulte o gerenciador de plugins do DSH; nenhuma política de segurança foi ignorada.',
|
|
47
49
|
'dsh-live-voice.commons.update.done':
|
|
48
50
|
'Voz ao Vivo {version} instalado. Recarregue a página quando quiser.',
|
|
49
51
|
'dsh-live-voice.commons.update.failed':
|
|
50
52
|
'Falha na atualização. A versão pode ainda não estar no npm. Verifique o gerenciador de plugins do DSH.',
|
|
53
|
+
'dsh-live-voice.commons.update.incompatible':
|
|
54
|
+
'Esta versão é incompatível com o DSH em execução. Consulte o gerenciador de plugins e use versões compatíveis do DSH e do plugin; nenhuma exceção de compatibilidade foi concedida.',
|
|
51
55
|
'dsh-live-voice.commons.update.installing': 'Atualizando…',
|
|
52
56
|
'dsh-live-voice.commons.update.label': 'Atualização disponível',
|
|
53
57
|
'dsh-live-voice.commons.update.link': 'Atualização disponível: {version}. Abrir lançamento',
|
|
@@ -42,9 +42,13 @@ const zh: LiveVoiceTranslation = {
|
|
|
42
42
|
'dsh-live-voice.commons.update.confirm':
|
|
43
43
|
'在此 DSH 配置中安装 实时语音 {version}?下载需要网络。若提示,请之后重启 DSH;不会自动重启。',
|
|
44
44
|
'dsh-live-voice.commons.update.current': '此版本已安装。请重启 DSH 后使用。',
|
|
45
|
+
'dsh-live-voice.commons.update.delayed':
|
|
46
|
+
'此配置暂时无法获取所请求的版本。它可能仍在发布,或受 pnpm minimumReleaseAge 限制。请稍后检查 DSH 插件管理器;未绕过任何安全策略。',
|
|
45
47
|
'dsh-live-voice.commons.update.done': '实时语音 {version} 已安装。准备好后请刷新页面。',
|
|
46
48
|
'dsh-live-voice.commons.update.failed':
|
|
47
49
|
'更新失败。该版本可能尚未发布到 npm。请查看 DSH 插件管理器。',
|
|
50
|
+
'dsh-live-voice.commons.update.incompatible':
|
|
51
|
+
'此版本与正在运行的 DSH 不兼容。请检查 DSH 插件管理器并使用兼容的 DSH 和插件版本;未授予兼容性豁免。',
|
|
48
52
|
'dsh-live-voice.commons.update.installing': '正在更新…',
|
|
49
53
|
'dsh-live-voice.commons.update.label': '有可用更新',
|
|
50
54
|
'dsh-live-voice.commons.update.link': '有可用更新:{version}。打开发布页面',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Conversation module
|
|
2
2
|
|
|
3
|
-
Transcription status separates detected speech (`recognizing`) from unsettled speech requests (`pendingTranscriptionRequests`, with legacy fallback to `pendingTranscriptions`). Aggregate `pendingTranscriptions` also includes storage/final-drain work for safety barriers; it must not be mistaken for visible transcription activity. Statuses are listening, listening and transcribing, waiting without speech, transcribing queued work without speech, and waiting for a command in Ignore mode. Playback remains separate; question and delivery countdown states retain their policy. Status copy has no ellipses. The compact recording indicator precedes the waveform and shows the committed audio frontier as seconds/minutes/unbounded hours, never wall-clock elapsed time; its saved-audio explanation is available only in the localized accessible name/tooltip.
|
|
3
|
+
Transcription status separates detected speech (`recognizing`) from unsettled speech requests (`pendingTranscriptionRequests`, with legacy fallback to `pendingTranscriptions`). Aggregate `pendingTranscriptions` also includes storage/final-drain work for safety barriers; it must not be mistaken for visible transcription activity. `pendingDeliveryTranscriptions` isolates speech-boundary bookkeeping and queued/active speech work from silent recording writes, so automatic-send countdowns in either source cannot starve on periodic persistence. Cross-source delivery gates use the same distinction; missing fields retain conservative legacy blocking. Statuses are listening, listening and transcribing, waiting without speech, transcribing queued work without speech, and waiting for a command in Ignore mode. Playback remains separate; question and delivery countdown states retain their policy. Status copy has no ellipses. The compact recording indicator precedes the waveform and shows the committed audio frontier as seconds/minutes/unbounded hours, never wall-clock elapsed time; its saved-audio explanation is available only in the localized accessible name/tooltip.
|
|
4
4
|
|
|
5
5
|
Final recognition chunks join with a space. A confirmed paragraph boundary adds exactly two newlines; source changes also start a new block. Timestamp/source prefixes appear only at the first block, source change or paragraph. Recognition completion time and normal source activity/settings updates do not create timestamp boundaries. Composer text is append-only; user edits remain untouched.
|
|
6
6
|
|
|
@@ -51,6 +51,8 @@ export class MeetingController {
|
|
|
51
51
|
if (Object.hasOwn(next, 'pending')) policy.pendingTranscriptions = next.pending;
|
|
52
52
|
if (Object.hasOwn(next, 'transcribing'))
|
|
53
53
|
policy.pendingTranscriptionRequests = next.transcribing;
|
|
54
|
+
if (Object.hasOwn(next, 'deliveryPending'))
|
|
55
|
+
policy.pendingDeliveryTranscriptions = next.deliveryPending;
|
|
54
56
|
input?.patch(policy);
|
|
55
57
|
}
|
|
56
58
|
this.snapshot = {
|
|
@@ -235,12 +237,16 @@ export class MeetingController {
|
|
|
235
237
|
this.onActivity?.(source, active);
|
|
236
238
|
input.refreshDeliveryReadiness();
|
|
237
239
|
},
|
|
238
|
-
onProcessingChange: ({ pending, transcribing }) => {
|
|
240
|
+
onProcessingChange: ({ pending, transcribing, deliveryPending }) => {
|
|
239
241
|
if (!valid()) return;
|
|
240
242
|
const requests =
|
|
241
243
|
Number.isSafeInteger(transcribing) && transcribing >= 0 ? transcribing : pending;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
+
const delivery =
|
|
245
|
+
Number.isSafeInteger(deliveryPending) && deliveryPending >= 0
|
|
246
|
+
? deliveryPending
|
|
247
|
+
: pending;
|
|
248
|
+
this.patch(source, { pending, transcribing: requests, deliveryPending: delivery });
|
|
249
|
+
if (delivery) input.cancelAutoSend({ preserveIntent: true });
|
|
244
250
|
else if (!input.getSnapshot().muted) input.maybeScheduleAutoSend();
|
|
245
251
|
},
|
|
246
252
|
onError: (error) => {
|
|
@@ -292,11 +298,18 @@ export class MeetingController {
|
|
|
292
298
|
recognizing: false,
|
|
293
299
|
pendingTranscriptions: 0,
|
|
294
300
|
pendingTranscriptionRequests: 0,
|
|
301
|
+
pendingDeliveryTranscriptions: 0,
|
|
295
302
|
});
|
|
296
303
|
this.onActivity?.(source, false);
|
|
297
304
|
job?.abort.abort();
|
|
298
305
|
this.transcript.setActive(source, false);
|
|
299
|
-
this.patch(source, {
|
|
306
|
+
this.patch(source, {
|
|
307
|
+
starting: false,
|
|
308
|
+
listening: false,
|
|
309
|
+
pending: 0,
|
|
310
|
+
transcribing: 0,
|
|
311
|
+
deliveryPending: 0,
|
|
312
|
+
});
|
|
300
313
|
await job?.engine?.stop();
|
|
301
314
|
await job?.meter?.stop();
|
|
302
315
|
}
|
|
@@ -56,6 +56,7 @@ export class VoiceCoordinator {
|
|
|
56
56
|
recognizing: false,
|
|
57
57
|
pendingTranscriptions: 0,
|
|
58
58
|
pendingTranscriptionRequests: undefined,
|
|
59
|
+
pendingDeliveryTranscriptions: undefined,
|
|
59
60
|
muted: normalizeSettings(settings).microphoneEnabled === false,
|
|
60
61
|
speaking: false,
|
|
61
62
|
paused: false,
|
|
@@ -440,15 +441,24 @@ export class VoiceCoordinator {
|
|
|
440
441
|
onResult: (result) => {
|
|
441
442
|
if (valid()) this.onResult(result);
|
|
442
443
|
},
|
|
443
|
-
onProcessingChange: ({ pending = 0, transcribing } = {}) => {
|
|
444
|
+
onProcessingChange: ({ pending = 0, transcribing, deliveryPending } = {}) => {
|
|
444
445
|
if (!valid()) return;
|
|
445
446
|
const count = Number.isSafeInteger(pending) && pending >= 0 ? pending : 0;
|
|
446
447
|
const requests =
|
|
447
448
|
Number.isSafeInteger(transcribing) && transcribing >= 0 ? transcribing : count;
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
449
|
+
const delivery =
|
|
450
|
+
Number.isSafeInteger(deliveryPending) && deliveryPending >= 0
|
|
451
|
+
? deliveryPending
|
|
452
|
+
: count;
|
|
453
|
+
this.patch({
|
|
454
|
+
pendingTranscriptions: count,
|
|
455
|
+
pendingTranscriptionRequests: requests,
|
|
456
|
+
pendingDeliveryTranscriptions: delivery,
|
|
457
|
+
});
|
|
458
|
+
if (delivery > 0) this.cancelAutoSend({ preserveIntent: true });
|
|
459
|
+
else if (this.holdToTalkRelease) {
|
|
460
|
+
if (count === 0) this._finishHoldToTalk();
|
|
461
|
+
} else this.maybeScheduleAutoSend();
|
|
452
462
|
if (count === 0) this._drain();
|
|
453
463
|
},
|
|
454
464
|
onActivity: (active) => {
|
|
@@ -713,6 +723,9 @@ export class VoiceCoordinator {
|
|
|
713
723
|
this._handleSpeechInterruption(this._speechActivity());
|
|
714
724
|
this._drain();
|
|
715
725
|
}
|
|
726
|
+
deliveryPending() {
|
|
727
|
+
return this.snapshot.pendingDeliveryTranscriptions ?? this.snapshot.pendingTranscriptions;
|
|
728
|
+
}
|
|
716
729
|
maybeScheduleAutoSend(draft = this.autoSendIntent) {
|
|
717
730
|
if (draft == null || draft !== this.composer.getDraft()) return;
|
|
718
731
|
if (this.autoSendTimer !== null) return;
|
|
@@ -721,7 +734,7 @@ export class VoiceCoordinator {
|
|
|
721
734
|
this.snapshot.muted ||
|
|
722
735
|
this.composer.canAutoSend?.() === false ||
|
|
723
736
|
this.snapshot.recognizing ||
|
|
724
|
-
this.
|
|
737
|
+
this.deliveryPending() > 0
|
|
725
738
|
)
|
|
726
739
|
return;
|
|
727
740
|
this.scheduleAutoSend(draft);
|
|
@@ -730,7 +743,7 @@ export class VoiceCoordinator {
|
|
|
730
743
|
if (
|
|
731
744
|
this.composer.canAutoSend?.() === false ||
|
|
732
745
|
this.snapshot.recognizing ||
|
|
733
|
-
this.
|
|
746
|
+
this.deliveryPending() > 0
|
|
734
747
|
)
|
|
735
748
|
this.cancelAutoSend({ preserveIntent: true });
|
|
736
749
|
else this.maybeScheduleAutoSend();
|
|
@@ -806,6 +819,7 @@ export class VoiceCoordinator {
|
|
|
806
819
|
recognizing: false,
|
|
807
820
|
pendingTranscriptions: 0,
|
|
808
821
|
pendingTranscriptionRequests: 0,
|
|
822
|
+
pendingDeliveryTranscriptions: 0,
|
|
809
823
|
starting: false,
|
|
810
824
|
});
|
|
811
825
|
return this._input(async () => {
|
|
@@ -175,6 +175,12 @@ export class WhisperHttpRecognitionEngine {
|
|
|
175
175
|
this.session = null;
|
|
176
176
|
session.signal?.removeEventListener('abort', session.abort);
|
|
177
177
|
await session.stopRecording();
|
|
178
|
-
session.onProcessingChange?.({
|
|
178
|
+
session.onProcessingChange?.({
|
|
179
|
+
queued: 0,
|
|
180
|
+
active: false,
|
|
181
|
+
transcribing: 0,
|
|
182
|
+
deliveryPending: 0,
|
|
183
|
+
pending: 0,
|
|
184
|
+
});
|
|
179
185
|
}
|
|
180
186
|
}
|
|
@@ -13,7 +13,7 @@ This browser-only module owns recording sessions, speech/silence pin markers, re
|
|
|
13
13
|
|
|
14
14
|
## Ownership and limits
|
|
15
15
|
|
|
16
|
-
Processing notifications
|
|
16
|
+
Processing notifications separate `transcribing` (queued plus active speech attempts), `deliveryPending` (those attempts plus unsettled speech-boundary writes and capture finish), and aggregate `pending` (all serialized writes). Only speech attempts drive visible transcription status. Automatic delivery waits for `deliveryPending`, not recurring silent PCM writes; Hold-to-Talk release, playback admission and page-leave protection still wait for aggregate work. Legacy engines without the delivery field conservatively use aggregate pending. Continuous silent PCM persistence must neither announce transcription nor reset the send countdown.
|
|
17
17
|
|
|
18
18
|
Append, pin, attempt, read and finish operations are serialized per writer. Append owns a copy of its input and rejects more than eight seconds of queued PCM as backpressure rather than buffering indefinitely. Finish immediately seals further audio and pins, drains accepted appends, flushes the partial page, then releases ownership. Attempt upserts may still settle after audio is sealed, for already-running recognition requests. Dispose interrupts and drains active writers before closing storage.
|
|
19
19
|
|
|
@@ -6,7 +6,13 @@ import type {
|
|
|
6
6
|
} from './types.js';
|
|
7
7
|
import type { SileroVadOptions, SileroVadStream } from '../recognition/vad/SileroVadRuntime.js';
|
|
8
8
|
type CaptureHandle = { finish(): Promise<void>; release(): void };
|
|
9
|
-
type Processing = {
|
|
9
|
+
type Processing = {
|
|
10
|
+
queued: number;
|
|
11
|
+
active: boolean;
|
|
12
|
+
transcribing: number;
|
|
13
|
+
deliveryPending: number;
|
|
14
|
+
pending: number;
|
|
15
|
+
};
|
|
10
16
|
interface RecognitionCallbacks {
|
|
11
17
|
signal?: AbortSignal;
|
|
12
18
|
lang?: string;
|
|
@@ -75,6 +81,7 @@ interface RecognitionSession {
|
|
|
75
81
|
onProcessingChange?: RecognitionCallbacks['onProcessingChange'];
|
|
76
82
|
tail: Promise<void>;
|
|
77
83
|
pendingWrites: number;
|
|
84
|
+
pendingSpeechWrites: number;
|
|
78
85
|
lang: string;
|
|
79
86
|
operation: string;
|
|
80
87
|
capturedSamples: number;
|
|
@@ -151,6 +158,7 @@ export async function startRecordingRecognition(
|
|
|
151
158
|
onProcessingChange,
|
|
152
159
|
tail: Promise.resolve(),
|
|
153
160
|
pendingWrites: 0,
|
|
161
|
+
pendingSpeechWrites: 0,
|
|
154
162
|
lang,
|
|
155
163
|
operation: uid(),
|
|
156
164
|
capturedSamples: 0,
|
|
@@ -170,7 +178,13 @@ export async function startRecordingRecognition(
|
|
|
170
178
|
active: !!session.activeRequest,
|
|
171
179
|
// Only queued/running speech attempts belong in the visible transcription status.
|
|
172
180
|
transcribing: session.transcriptionQueue.length + (session.activeRequest ? 1 : 0),
|
|
173
|
-
//
|
|
181
|
+
// Speech-boundary bookkeeping must settle before delivery, but silent PCM writes must not reset it.
|
|
182
|
+
deliveryPending:
|
|
183
|
+
session.transcriptionQueue.length +
|
|
184
|
+
(session.activeRequest ? 1 : 0) +
|
|
185
|
+
session.pendingSpeechWrites +
|
|
186
|
+
(session.finishing ? 1 : 0),
|
|
187
|
+
// All writes still protect release, playback admission and page leave.
|
|
174
188
|
pending:
|
|
175
189
|
session.transcriptionQueue.length +
|
|
176
190
|
(session.activeRequest ? 1 : 0) +
|
|
@@ -184,14 +198,16 @@ export async function startRecordingRecognition(
|
|
|
184
198
|
onError?.(new Error(engine.recordingErrorMessage(error)));
|
|
185
199
|
void engine.stop();
|
|
186
200
|
};
|
|
187
|
-
const serialize = (task: () => Promise<void>, preserve = false) => {
|
|
201
|
+
const serialize = (task: () => Promise<void>, preserve = false, speech = false) => {
|
|
188
202
|
session.pendingWrites++;
|
|
203
|
+
if (speech) session.pendingSpeechWrites++;
|
|
189
204
|
notify();
|
|
190
205
|
const work = session.tail.then(async () => {
|
|
191
206
|
if (preserve || valid()) await task();
|
|
192
207
|
});
|
|
193
208
|
session.tail = work.catch(failure).finally(() => {
|
|
194
209
|
session.pendingWrites--;
|
|
210
|
+
if (speech) session.pendingSpeechWrites--;
|
|
195
211
|
notify();
|
|
196
212
|
});
|
|
197
213
|
return work;
|
|
@@ -211,10 +227,14 @@ export async function startRecordingRecognition(
|
|
|
211
227
|
if (through - Math.max(liveSpeechStart, lastSplitQueued) < max) return;
|
|
212
228
|
const end = Math.max(liveSpeechStart, lastSplitQueued) + max;
|
|
213
229
|
lastSplitQueued = end;
|
|
214
|
-
void serialize(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
230
|
+
void serialize(
|
|
231
|
+
async () => {
|
|
232
|
+
const speech = [...writer.session.pins].reverse().find((p) => p.kind === 'speech-start');
|
|
233
|
+
if (speech && valid()) await queueSpeech(speech, end);
|
|
234
|
+
},
|
|
235
|
+
false,
|
|
236
|
+
true,
|
|
237
|
+
).catch(() => {});
|
|
218
238
|
};
|
|
219
239
|
const queueSpeech = async (pin: RecordingPin, end: number) => {
|
|
220
240
|
for (let at = Math.max(pin.atSample, queuedThrough); at < end; at += max) {
|
|
@@ -260,19 +280,23 @@ export async function startRecordingRecognition(
|
|
|
260
280
|
liveSpeechStart = startSample;
|
|
261
281
|
lastSplitQueued = startSample;
|
|
262
282
|
} else liveSpeechStart = null;
|
|
263
|
-
void serialize(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
283
|
+
void serialize(
|
|
284
|
+
async () => {
|
|
285
|
+
const previous = writer.session.pins.at(-1);
|
|
286
|
+
if (
|
|
287
|
+
kind === 'speech-start' &&
|
|
288
|
+
previous?.kind === 'silence-start' &&
|
|
289
|
+
previous.atSample > 0 &&
|
|
290
|
+
startSample - previous.atSample >= paragraphSamples
|
|
291
|
+
)
|
|
292
|
+
paragraphStarts.add(startSample);
|
|
293
|
+
await writer.pin(kind, startSample, confirmedSample);
|
|
294
|
+
if (valid() && kind === 'silence-start' && previous?.kind === 'speech-start')
|
|
295
|
+
await queueSpeech(previous, startSample);
|
|
296
|
+
},
|
|
297
|
+
true,
|
|
298
|
+
true,
|
|
299
|
+
).catch(() => {});
|
|
276
300
|
};
|
|
277
301
|
const detector = new PinDetector({
|
|
278
302
|
sampleRate: capture.silero ? 16000 : rate,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Settings module
|
|
2
2
|
|
|
3
|
-
`services/pluginUpdate.ts` owns an application-lifetime explicit update state machine, passed into Settings by the app composition root. Opening Settings only checks GitHub; Update now reads profile eligibility, then requires a separate exact-version confirmation before the official DSH manager installs. Development/custom sources are never replaced; uncertain outcomes lock retries until the person checks the manager. Closing Settings preserves operation state. Installation never grants build-script permissions or restarts/reloads automatically. The inspected DSH manager returns restart-required for existing profile dependencies, so the running plugin is not replaced during download. Voice activity is checked immediately before submission, not reserved across the download or across browser clients. The manager offers no expected-source/CAS option: do not change this dependency through another manager/CLI concurrently with confirmation. Source is rechecked at confirmation but cannot be atomically guarded by this client.
|
|
3
|
+
`services/pluginUpdate.ts` owns an application-lifetime explicit update state machine, passed into Settings by the app composition root. Opening Settings only checks GitHub; Update now reads profile eligibility, then requires a separate exact-version confirmation before the official DSH manager installs. Development/custom sources are never replaced; uncertain outcomes lock retries until the person checks the manager. Closing Settings preserves operation state. Installation never grants build-script permissions or restarts/reloads automatically. Classify structured manager failures for blocked scripts, unavailable versions (including possible publication/release-age delays), and incompatible DSH versions; do not infer a successful update from installed files alone. The three-hour GitHub detection cache is independent of installation and remains unchanged. The inspected DSH manager returns restart-required for existing profile dependencies, so the running plugin is not replaced during download. Voice activity is checked immediately before submission, not reserved across the download or across browser clients. The manager offers no expected-source/CAS option: do not change this dependency through another manager/CLI concurrently with confirmation. Source is rechecked at confirmation but cannot be atomically guarded by this client.
|
|
4
4
|
|
|
5
5
|
`services/debuggerInstall.ts` owns the separate application-lifetime, explicitly confirmed installation of the matching-version optional debugger through the same remote DSH manager. It rechecks inventory and voice activity before submission and refuses every existing debugger dependency, including disabled registry installs and unreadable custom sources. Submitted failures or uncertain replies lock retries for manager review. The manager has no expected-source/CAS option: do not change the debugger through another manager or CLI during confirmation/installation. `components/DebuggerLauncher.tsx` keeps Diagnostics in the fixed footer without importing the debugger bundle, explains the dependency in a native accessible dialog, and observes the versioned developer-extension registration before reporting ready. An applied result alone is not proof of browser readiness; restart-required results are shown truthfully without restarting or reloading. Dialog styles are feature-scoped.
|
|
6
6
|
|
|
@@ -92,7 +92,16 @@ export function createPluginUpdate({
|
|
|
92
92
|
if (!result?.ok) return patch({ ...state, phase: 'uncertain', problem: 'uncertain' });
|
|
93
93
|
const value = result.value;
|
|
94
94
|
if (value.application === 'failed' || value.application === 'cancelled')
|
|
95
|
-
return fail(
|
|
95
|
+
return fail(
|
|
96
|
+
value.error?.code === 'incompatible-version' ||
|
|
97
|
+
value.packageResult?.incompatible?.length
|
|
98
|
+
? 'incompatible'
|
|
99
|
+
: value.pendingBuilds?.length || value.packageResult?.kind === 'build-blocked'
|
|
100
|
+
? 'scripts'
|
|
101
|
+
: ['not-found', 'no-matching-version'].includes(value.packageResult?.kind)
|
|
102
|
+
? 'delayed'
|
|
103
|
+
: 'failed',
|
|
104
|
+
);
|
|
96
105
|
if (
|
|
97
106
|
value.bundle !== 'dsh-live-voice' ||
|
|
98
107
|
value.version !== state.version ||
|