@markusylisiurunen/tau 0.3.53 → 0.3.55
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/dist/core/commands/registry.js +10 -5
- package/dist/core/commands/registry.js.map +1 -1
- package/dist/core/config/index.js +1 -1
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/schema.js +10 -2
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/diff_review/bridge.js +2 -1
- package/dist/core/diff_review/bridge.js.map +1 -1
- package/dist/core/diff_review/tool.js +1 -1
- package/dist/core/diff_review/tool.js.map +1 -1
- package/dist/core/static/tau_docs/config-reference.md +6 -6
- package/dist/core/static/tau_docs/credentials.md +3 -2
- package/dist/core/static/tau_docs/telegram.md +4 -3
- package/dist/core/static/tau_docs/troubleshooting.md +1 -1
- package/dist/core/static/tau_docs/tui.md +5 -3
- package/dist/core/telegram/adapter.js +40 -20
- package/dist/core/telegram/adapter.js.map +1 -1
- package/dist/core/telegram/cli.js +4 -2
- package/dist/core/telegram/cli.js.map +1 -1
- package/dist/core/telegram/runtime.js +1 -0
- package/dist/core/telegram/runtime.js.map +1 -1
- package/dist/core/utils/gemini_transcription.js +14 -15
- package/dist/core/utils/gemini_transcription.js.map +1 -1
- package/dist/core/utils/mistral_transcription.js +6 -2
- package/dist/core/utils/mistral_transcription.js.map +1 -1
- package/dist/core/utils/openai_transcription.js +431 -0
- package/dist/core/utils/openai_transcription.js.map +1 -0
- package/dist/core/utils/speech_to_text.js +66 -14
- package/dist/core/utils/speech_to_text.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/diff_tool/app/dist/assets/{angular-html-vSIwre92.js → angular-html-DoxI_och.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{angular-ts-B9yWa-IT.js → angular-ts-C7CUd-D2.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{apl-CBg9ewu1.js → apl-7dieYM-l.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{astro-BVjxSYjj.js → astro-Czj3O4jM.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{blade-jvqV5kiv.js → blade-ClS0E2cC.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{c-DDAfW08c.js → c-BKvTh-Jt.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{chapel-Qflwfgbt.js → chapel-qvFrfwEr.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{cobol-4iJI1LUg.js → cobol-B0vsaB-J.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{coffee-C9LbGGO3.js → coffee-BUlcrU75.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{cpp-B-x6veTx.js → cpp-CKqEgX1l.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{crystal-S06EUmKn.js → crystal-FCqSmIJ_.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{css-DA4RYriG.js → css-zZvYFJFc.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{edge-7dx6BjGp.js → edge-BKlbCo1f.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{elixir-DPywG97a.js → elixir-B2gSxtgZ.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{elm-Dgo_WnqC.js → elm-DFEdzPJz.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{erb-CVW4PjFl.js → erb-DSgn8fHS.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{git-rebase-CtU0gmBJ.js → git-rebase-CcgDECQr.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{glimmer-js-twAYiPP2.js → glimmer-js-Dwxi4cX2.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{glimmer-ts-BfNoXayN.js → glimmer-ts-BnJVER8W.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{glsl-D_XrtFF7.js → glsl-z7_gU-HN.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{graphql-B_GeapzM.js → graphql-Dt7V-d3L.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{hack-BCaCVoSX.js → hack-jVkr6EC8.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{haml-z9Mz7I1O.js → haml-BF7cRiOF.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{handlebars-D0FVYagc.js → handlebars-DAhNxR3E.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{html-BOM4tb7u.js → html-B7QW5YvU.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{html-derivative-CsSlhJgr.js → html-derivative-B-vyaE-N.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{http-Cjy9oaQn.js → http-Drkl04RP.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{hurl-CjLQ7ql5.js → hurl-7gTFrvcN.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{index-QO81yN6b.js → index-C9CiIimI.js} +41 -41
- package/dist/diff_tool/app/dist/assets/{java-cn8DiFD8.js → java-ZmvXuvs3.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{javascript-CEnHZaZM.js → javascript-C-OECu_Z.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{jinja-DIp6kVk7.js → jinja-hvkV87CG.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{jison-Dx116ito.js → jison-eJBDxKI8.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{json-C6B9h0n5.js → json-BPQU2w0u.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{jsx-CDEGT2B2.js → jsx-C19s5MOj.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{julia-CsIJA3Km.js → julia-CYISvCwi.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{just-DZRYvkQw.js → just-5omBg5pB.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{latex-CeR-RQOI.js → latex-B_L85F2z.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{liquid-Dhf8N4mn.js → liquid-BxbaQPPS.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{lua-zVTVaJzn.js → lua-DQA0z-44.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{marko-BGPq5r3o.js → marko-Dj5RV8IH.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{mdc-D8K78bym.js → mdc-CoHI6VJN.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{mojo-CFuIV2fl.js → mojo-fg40_ntQ.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{nginx-Bo-bEa75.js → nginx-COnrY-gb.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{nim-Bfy5M54r.js → nim-Ccfp0p1j.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{org-BKetN7sT.js → org-RForNsWm.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{perl-WiwTOQTV.js → perl-DhHRsiUu.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{php-CM2FzhGV.js → php-BlxKhJek.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{pug-D2WvUHZ-.js → pug-o9YxnGzH.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{qml-DGPbc6TL.js → qml-Dzf494vL.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{r-CCFYJ_KM.js → r-Dp0OUrF6.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{razor-D1JuQWQz.js → razor-gylahu-m.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{regexp-zTiHwMow.js → regexp-C0rsDjCm.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{rst-CuUAArfH.js → rst-D-DRAFtq.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{ruby-DznrLCoG.js → ruby-BnY40vXq.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{sas-D2hJmLva.js → sas-BaSo_s7i.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{scss-Cjb3p_6n.js → scss-BX5R-Yyw.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{shellscript-BtwI-tfX.js → shellscript-CpRFVBRj.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{shellsession-BYirS1xP.js → shellsession-BGRUDtms.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{soy-D4Ndk7-j.js → soy-C2WL9FSb.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{sql-DbLQ5SJl.js → sql-CGmNnOfz.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{stata-BXoNBosJ.js → stata-CDamV3Uz.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{surrealql-toyTYnAL.js → surrealql-BqI4o6tP.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{svelte-BEpa7pAW.js → svelte-DPaOUioa.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{templ-CZfW_RJI.js → templ-aqrZIEoc.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{tex-COemmIIu.js → tex-Bjjtfacp.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{ts-tags-CMVUY_BA.js → ts-tags-BGFnsvdQ.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{tsx-BTvP6uzd.js → tsx-C_rOxPqZ.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{twig-DzP5RjJc.js → twig-DYqVwCOf.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{typescript-CQaGlMoX.js → typescript-CppQYc7K.js} +1 -1
- package/dist/diff_tool/app/dist/assets/typst-KIbA0FZG.js +1 -0
- package/dist/diff_tool/app/dist/assets/{vue-html-BA_OH0Xt.js → vue-html-DcZpMui9.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{vue-DaVWTtvb.js → vue-iOYoJEJi.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{vue-vine-C_H4XbNR.js → vue-vine-C6v6c0Lr.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{xml-1JLzpunp.js → xml-zR0RltJO.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{xsl-GtZi0Erj.js → xsl-B9pBLsIV.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{yaml-LueTtDDo.js → yaml-sicIihvu.js} +1 -1
- package/dist/diff_tool/app/dist/index.html +1 -1
- package/dist/tui/listen_capture.js +84 -20
- package/dist/tui/listen_capture.js.map +1 -1
- package/dist/tui/session_chat_controller.js +213 -29
- package/dist/tui/session_chat_controller.js.map +1 -1
- package/dist/tui/speech_playback.js +3 -1
- package/dist/tui/speech_playback.js.map +1 -1
- package/dist/tui/ui/footer.js +6 -6
- package/dist/tui/ui/footer.js.map +1 -1
- package/package.json +9 -9
- package/dist/diff_tool/app/dist/assets/typst-DI99ib-x.js +0 -1
|
@@ -24,9 +24,10 @@ import { InterruptLifecycle } from "./chat_controller/interrupt_lifecycle.js";
|
|
|
24
24
|
import { formatDurationMs, formatFooterTokenCount, formatSessionCost, } from "./chat_controller/status_format.js";
|
|
25
25
|
import { copyTextToClipboard } from "./clipboard.js";
|
|
26
26
|
import { DOUBLE_PRESS_WINDOW_MS } from "./constants.js";
|
|
27
|
-
import { cleanupListenTempFile, createListenTempFilePath, getSpeechToTextApiKey, getSpeechToTextApiKeyErrorMessage, LISTEN_RECORDING_MAX_DURATION_MS, LISTEN_RECORDING_MIN_BYTES, readListenAudio, startListenAudioCapture,
|
|
27
|
+
import { cleanupListenTempFile, createListenTempFilePath, createListenTranscription, deleteListenTempFile, getSpeechToTextApiKey, getSpeechToTextApiKeyErrorMessage, LISTEN_CAPTURE_START_TIMEOUT_MS, LISTEN_RECORDING_MAX_DURATION_MS, LISTEN_RECORDING_MIN_BYTES, readListenAudio, startListenAudioCapture, } from "./listen_capture.js";
|
|
28
28
|
import { createSdkDiffSnapshotDeps, createSdkToolExecutionBackend, } from "./session_tool_execution_backend.js";
|
|
29
29
|
import { runSpeechPlaybackTask } from "./speech_playback.js";
|
|
30
|
+
const LISTEN_CAPTURE_START_CANCELLED = Symbol("listen capture start cancelled");
|
|
30
31
|
export class SessionChatController {
|
|
31
32
|
view;
|
|
32
33
|
session;
|
|
@@ -37,6 +38,7 @@ export class SessionChatController {
|
|
|
37
38
|
defaultDiffTool;
|
|
38
39
|
diffToolLauncher;
|
|
39
40
|
deps;
|
|
41
|
+
speechToTextDeps;
|
|
40
42
|
themeIds;
|
|
41
43
|
commandRegistry;
|
|
42
44
|
commandHandlers;
|
|
@@ -73,8 +75,11 @@ export class SessionChatController {
|
|
|
73
75
|
turnTimer;
|
|
74
76
|
lastEmptySubmitAt;
|
|
75
77
|
listenRecording;
|
|
78
|
+
retainedListenAudioPath;
|
|
76
79
|
listenTransition;
|
|
77
|
-
|
|
80
|
+
listenStartupAbortController;
|
|
81
|
+
activeListenTranscription;
|
|
82
|
+
listenActivityLabel;
|
|
78
83
|
speechActivityLabel;
|
|
79
84
|
speakTask;
|
|
80
85
|
constructor(options) {
|
|
@@ -89,6 +94,7 @@ export class SessionChatController {
|
|
|
89
94
|
this.defaultDiffTool = options.defaultDiffTool;
|
|
90
95
|
this.diffToolLauncher = options.diffToolLauncher;
|
|
91
96
|
this.deps = options.deps ?? createDefaultCoreDeps();
|
|
97
|
+
this.speechToTextDeps = options.speechToTextDeps;
|
|
92
98
|
this.themeIds = options.themeIds ?? [];
|
|
93
99
|
this.observedSessionRevision = options.snapshot.revision;
|
|
94
100
|
this.commandRegistry = createCommandRegistry();
|
|
@@ -104,7 +110,7 @@ export class SessionChatController {
|
|
|
104
110
|
compactSummaryOnly: (extra) => this.compactSession("summary-only", extra),
|
|
105
111
|
compactSummaryAndLast: (extra) => this.compactSession("summary-and-last", extra),
|
|
106
112
|
reload: () => this.reloadContent(),
|
|
107
|
-
listen: () => this.
|
|
113
|
+
listen: (action) => this.handleListenCommand(action),
|
|
108
114
|
speak: () => this.speakLastAssistantMessage(),
|
|
109
115
|
persona: (id) => this.setPersona(id),
|
|
110
116
|
prompt: (id) => this.insertPrompt(id),
|
|
@@ -142,6 +148,8 @@ export class SessionChatController {
|
|
|
142
148
|
this.ephemeralUnsubscribe?.();
|
|
143
149
|
this.pendingUserMessagesUnsubscribe?.();
|
|
144
150
|
this.subagentActivitiesUnsubscribe?.();
|
|
151
|
+
this.listenStartupAbortController?.abort(LISTEN_CAPTURE_START_CANCELLED);
|
|
152
|
+
this.activeListenTranscription?.abort();
|
|
145
153
|
if (this.listenTransition) {
|
|
146
154
|
await this.listenTransition;
|
|
147
155
|
}
|
|
@@ -524,6 +532,11 @@ export class SessionChatController {
|
|
|
524
532
|
this.view.showFooterNotice("interrupted", "default");
|
|
525
533
|
return;
|
|
526
534
|
}
|
|
535
|
+
if (this.listenStartupAbortController) {
|
|
536
|
+
this.listenStartupAbortController.abort(LISTEN_CAPTURE_START_CANCELLED);
|
|
537
|
+
this.view.showFooterNotice("interrupted", "default");
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
527
540
|
if (this.listenRecording) {
|
|
528
541
|
void this.runListenTransition(() => this.stopListenCapture());
|
|
529
542
|
return;
|
|
@@ -552,9 +565,28 @@ export class SessionChatController {
|
|
|
552
565
|
]);
|
|
553
566
|
}
|
|
554
567
|
}
|
|
568
|
+
async handleListenCommand(action) {
|
|
569
|
+
switch (action) {
|
|
570
|
+
case "record":
|
|
571
|
+
await this.startListenCaptureFromCommand();
|
|
572
|
+
return;
|
|
573
|
+
case "retry":
|
|
574
|
+
await this.retryRetainedListenAudio();
|
|
575
|
+
return;
|
|
576
|
+
case "discard":
|
|
577
|
+
await this.discardRetainedListenAudio();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
555
581
|
async toggleListenCapture() {
|
|
556
582
|
if (this.listenTransition) {
|
|
557
|
-
this.
|
|
583
|
+
if (this.listenStartupAbortController) {
|
|
584
|
+
this.listenStartupAbortController.abort(LISTEN_CAPTURE_START_CANCELLED);
|
|
585
|
+
this.view.showFooterNotice("cancelled speech recording startup", "default");
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
this.view.showFooterNotice("speech recording state change already in progress", "default");
|
|
589
|
+
}
|
|
558
590
|
return;
|
|
559
591
|
}
|
|
560
592
|
if (this.listenRecording) {
|
|
@@ -572,7 +604,7 @@ export class SessionChatController {
|
|
|
572
604
|
this.view.showFooterNotice("speech recording already in progress", "default");
|
|
573
605
|
return;
|
|
574
606
|
}
|
|
575
|
-
if (this.
|
|
607
|
+
if (this.listenActivityLabel) {
|
|
576
608
|
this.view.showFooterNotice("speech transcription already in progress", "default");
|
|
577
609
|
return;
|
|
578
610
|
}
|
|
@@ -605,20 +637,55 @@ export class SessionChatController {
|
|
|
605
637
|
]);
|
|
606
638
|
return;
|
|
607
639
|
}
|
|
640
|
+
const retainedAudioPath = this.retainedListenAudioPath;
|
|
608
641
|
let audioPath;
|
|
642
|
+
let transcription;
|
|
643
|
+
let abortController;
|
|
644
|
+
let completion;
|
|
609
645
|
try {
|
|
610
646
|
audioPath = await createListenTempFilePath(this.deps);
|
|
611
|
-
|
|
612
|
-
|
|
647
|
+
transcription = this.createSpeechTranscription("streaming");
|
|
648
|
+
abortController = new AbortController();
|
|
649
|
+
this.listenStartupAbortController = abortController;
|
|
650
|
+
const capture = startListenAudioCapture({
|
|
613
651
|
deps: this.deps,
|
|
614
652
|
audioPath,
|
|
615
653
|
signal: abortController.signal,
|
|
654
|
+
onAudioChunk: (audio) => transcription?.appendAudio(audio),
|
|
616
655
|
});
|
|
656
|
+
completion = capture.completion;
|
|
657
|
+
await this.waitForListenCaptureStart(capture.started, abortController);
|
|
658
|
+
if (abortController.signal.aborted) {
|
|
659
|
+
throw abortController.signal.reason;
|
|
660
|
+
}
|
|
661
|
+
if (this.listenStartupAbortController === abortController) {
|
|
662
|
+
this.listenStartupAbortController = undefined;
|
|
663
|
+
}
|
|
664
|
+
if (retainedAudioPath) {
|
|
665
|
+
try {
|
|
666
|
+
await deleteListenTempFile(retainedAudioPath);
|
|
667
|
+
if (this.retainedListenAudioPath === retainedAudioPath) {
|
|
668
|
+
this.retainedListenAudioPath = undefined;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
abortController.abort();
|
|
673
|
+
transcription.abort();
|
|
674
|
+
await completion.catch(() => undefined);
|
|
675
|
+
await cleanupListenTempFile(audioPath);
|
|
676
|
+
this.view.addTranscriptNotice("failed to replace retained recording", "error", [
|
|
677
|
+
error.message,
|
|
678
|
+
`recording retained at ${retainedAudioPath}`,
|
|
679
|
+
]);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
617
683
|
const recording = {
|
|
618
684
|
audioPath,
|
|
619
685
|
stopRequested: false,
|
|
620
686
|
abortController,
|
|
621
687
|
completion,
|
|
688
|
+
transcription,
|
|
622
689
|
};
|
|
623
690
|
recording.maxDurationTimeout = setTimeout(() => {
|
|
624
691
|
if (this.listenRecording !== recording || this.listenTransition)
|
|
@@ -631,10 +698,43 @@ export class SessionChatController {
|
|
|
631
698
|
void this.watchListenRecording(recording);
|
|
632
699
|
}
|
|
633
700
|
catch (err) {
|
|
701
|
+
const cancelled = abortController?.signal.reason === LISTEN_CAPTURE_START_CANCELLED;
|
|
702
|
+
abortController?.abort();
|
|
703
|
+
transcription?.abort();
|
|
704
|
+
await completion?.catch(() => undefined);
|
|
634
705
|
if (audioPath) {
|
|
635
706
|
await cleanupListenTempFile(audioPath);
|
|
636
707
|
}
|
|
637
|
-
|
|
708
|
+
if (!cancelled) {
|
|
709
|
+
this.view.addTranscriptNotice("failed to start recording", "error", [
|
|
710
|
+
err.message,
|
|
711
|
+
]);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
finally {
|
|
715
|
+
if (this.listenStartupAbortController === abortController) {
|
|
716
|
+
this.listenStartupAbortController = undefined;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
async waitForListenCaptureStart(started, abortController) {
|
|
721
|
+
let timeout;
|
|
722
|
+
try {
|
|
723
|
+
await Promise.race([
|
|
724
|
+
started,
|
|
725
|
+
new Promise((_resolve, reject) => {
|
|
726
|
+
timeout = setTimeout(() => {
|
|
727
|
+
const error = new Error("timed out waiting for microphone audio");
|
|
728
|
+
abortController.abort(error);
|
|
729
|
+
reject(error);
|
|
730
|
+
}, LISTEN_CAPTURE_START_TIMEOUT_MS);
|
|
731
|
+
timeout.unref?.();
|
|
732
|
+
}),
|
|
733
|
+
]);
|
|
734
|
+
}
|
|
735
|
+
finally {
|
|
736
|
+
if (timeout)
|
|
737
|
+
clearTimeout(timeout);
|
|
638
738
|
}
|
|
639
739
|
}
|
|
640
740
|
async stopListenCapture() {
|
|
@@ -651,39 +751,120 @@ export class SessionChatController {
|
|
|
651
751
|
await recording.completion;
|
|
652
752
|
}
|
|
653
753
|
catch (err) {
|
|
754
|
+
recording.transcription.abort();
|
|
654
755
|
this.view.addTranscriptNotice("failed to record audio", "error", [err.message]);
|
|
655
756
|
await cleanupListenTempFile(recording.audioPath);
|
|
656
757
|
return;
|
|
657
758
|
}
|
|
658
|
-
this.
|
|
659
|
-
|
|
759
|
+
await this.transcribeListenAudioFile(recording.audioPath, recording.transcription);
|
|
760
|
+
}
|
|
761
|
+
async retryRetainedListenAudio() {
|
|
762
|
+
if (this.listenRecording || this.listenTransition || this.listenActivityLabel) {
|
|
763
|
+
this.view.showFooterNotice("speech recording state change already in progress", "default");
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
const audioPath = this.retainedListenAudioPath;
|
|
767
|
+
if (!audioPath) {
|
|
768
|
+
this.view.showFooterNotice("no failed speech recording to retry", "default");
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
await this.runListenTransition(() => this.transcribeListenAudioFile(audioPath));
|
|
772
|
+
}
|
|
773
|
+
async discardRetainedListenAudio() {
|
|
774
|
+
if (this.listenRecording || this.listenTransition || this.listenActivityLabel) {
|
|
775
|
+
this.view.showFooterNotice("speech recording state change already in progress", "default");
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const audioPath = this.retainedListenAudioPath;
|
|
779
|
+
if (!audioPath) {
|
|
780
|
+
this.view.showFooterNotice("no failed speech recording to discard", "default");
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
660
783
|
try {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
784
|
+
await deleteListenTempFile(audioPath);
|
|
785
|
+
this.retainedListenAudioPath = undefined;
|
|
786
|
+
this.view.showFooterNotice("discarded retained speech recording", "default");
|
|
787
|
+
}
|
|
788
|
+
catch (error) {
|
|
789
|
+
this.view.addTranscriptNotice("failed to discard speech recording", "error", [
|
|
790
|
+
error.message,
|
|
791
|
+
`recording retained at ${audioPath}`,
|
|
792
|
+
]);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
createSpeechTranscription(mode) {
|
|
796
|
+
return createListenTranscription({
|
|
797
|
+
config: this.config,
|
|
798
|
+
deps: this.deps,
|
|
799
|
+
mode,
|
|
800
|
+
context: collectSpeechToTextContext(this.snapshot),
|
|
801
|
+
speechToTextDeps: this.speechToTextDeps,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
async transcribeListenAudioFile(audioPath, transcription) {
|
|
805
|
+
let audio;
|
|
806
|
+
try {
|
|
807
|
+
audio = await readListenAudio(audioPath);
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
transcription?.abort();
|
|
811
|
+
this.view.addTranscriptNotice("failed to read speech recording", "error", [
|
|
812
|
+
error.message,
|
|
813
|
+
]);
|
|
814
|
+
if (this.retainedListenAudioPath === audioPath) {
|
|
815
|
+
this.retainedListenAudioPath = undefined;
|
|
665
816
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
817
|
+
await cleanupListenTempFile(audioPath);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (audio.byteLength < LISTEN_RECORDING_MIN_BYTES) {
|
|
821
|
+
transcription?.abort();
|
|
822
|
+
this.view.showFooterNotice("recording too short, try again", "default");
|
|
823
|
+
if (this.retainedListenAudioPath === audioPath) {
|
|
824
|
+
this.retainedListenAudioPath = undefined;
|
|
825
|
+
}
|
|
826
|
+
await cleanupListenTempFile(audioPath);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
let activeTranscription = transcription;
|
|
830
|
+
this.listenActivityLabel = "transcribing voice input";
|
|
831
|
+
this.refreshStatus();
|
|
832
|
+
try {
|
|
833
|
+
activeTranscription ??= this.createSpeechTranscription("file");
|
|
834
|
+
this.activeListenTranscription = activeTranscription;
|
|
835
|
+
const text = await activeTranscription.finish({
|
|
669
836
|
audio,
|
|
670
|
-
|
|
837
|
+
mimeType: "audio/wav",
|
|
838
|
+
fileName: "speech.wav",
|
|
839
|
+
language: "en",
|
|
671
840
|
});
|
|
672
|
-
const text = transcript.trim();
|
|
673
|
-
if (!text) {
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
841
|
this.view.insertEditorTextAtCursor(text);
|
|
842
|
+
this.retainedListenAudioPath = undefined;
|
|
843
|
+
try {
|
|
844
|
+
await deleteListenTempFile(audioPath);
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
this.view.addTranscriptNotice("failed to delete speech recording", "error", [
|
|
848
|
+
error.message,
|
|
849
|
+
`recording remains at ${audioPath}; delete it manually`,
|
|
850
|
+
]);
|
|
851
|
+
}
|
|
677
852
|
}
|
|
678
|
-
catch (
|
|
853
|
+
catch (error) {
|
|
854
|
+
this.retainedListenAudioPath = audioPath;
|
|
679
855
|
this.view.addTranscriptNotice("failed to transcribe speech", "error", [
|
|
680
|
-
|
|
856
|
+
error.message,
|
|
857
|
+
`recording retained at ${audioPath}`,
|
|
858
|
+
"run /listen retry to try again, or /listen discard to delete it",
|
|
681
859
|
]);
|
|
682
860
|
}
|
|
683
861
|
finally {
|
|
684
|
-
|
|
862
|
+
activeTranscription?.abort();
|
|
863
|
+
if (this.activeListenTranscription === activeTranscription) {
|
|
864
|
+
this.activeListenTranscription = undefined;
|
|
865
|
+
}
|
|
866
|
+
this.listenActivityLabel = undefined;
|
|
685
867
|
this.refreshStatus();
|
|
686
|
-
await cleanupListenTempFile(recording.audioPath);
|
|
687
868
|
}
|
|
688
869
|
}
|
|
689
870
|
async cancelListenCapture() {
|
|
@@ -696,6 +877,7 @@ export class SessionChatController {
|
|
|
696
877
|
this.view.setEditorInputEnabled(true);
|
|
697
878
|
this.refreshStatus();
|
|
698
879
|
recording.abortController.abort();
|
|
880
|
+
recording.transcription.abort();
|
|
699
881
|
try {
|
|
700
882
|
await recording.completion;
|
|
701
883
|
}
|
|
@@ -711,6 +893,7 @@ export class SessionChatController {
|
|
|
711
893
|
if (this.listenRecording !== recording || recording.stopRequested)
|
|
712
894
|
return;
|
|
713
895
|
this.listenRecording = undefined;
|
|
896
|
+
recording.transcription.abort();
|
|
714
897
|
this.view.setEditorInputEnabled(true);
|
|
715
898
|
this.refreshStatus();
|
|
716
899
|
const detail = result.exitCode !== null
|
|
@@ -726,6 +909,7 @@ export class SessionChatController {
|
|
|
726
909
|
if (this.listenRecording !== recording || recording.stopRequested)
|
|
727
910
|
return;
|
|
728
911
|
this.listenRecording = undefined;
|
|
912
|
+
recording.transcription.abort();
|
|
729
913
|
this.view.setEditorInputEnabled(true);
|
|
730
914
|
this.refreshStatus();
|
|
731
915
|
const error = err;
|
|
@@ -1603,8 +1787,8 @@ export class SessionChatController {
|
|
|
1603
1787
|
if (this.manualCompactionInProgress || this.hasRunningCompactionOperation(this.snapshot)) {
|
|
1604
1788
|
return "compacting context";
|
|
1605
1789
|
}
|
|
1606
|
-
if (this.
|
|
1607
|
-
return
|
|
1790
|
+
if (this.listenActivityLabel) {
|
|
1791
|
+
return this.listenActivityLabel;
|
|
1608
1792
|
}
|
|
1609
1793
|
return this.diffReviewService.getActivityLabel(this.speechActivityLabel);
|
|
1610
1794
|
}
|
|
@@ -1772,7 +1956,7 @@ export class SessionChatController {
|
|
|
1772
1956
|
return (!this.isSessionOperationActive() &&
|
|
1773
1957
|
!this.listenRecording &&
|
|
1774
1958
|
!this.listenTransition &&
|
|
1775
|
-
!this.
|
|
1959
|
+
!this.listenActivityLabel &&
|
|
1776
1960
|
!this.speakTask);
|
|
1777
1961
|
}
|
|
1778
1962
|
resolveDiffToolConfig() {
|