From 971212aa6c870f9332f0c14a6e566c2af22f9876 Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 9 Aug 2026 21:04:15 +0200 Subject: [PATCH] Fix output vuoto: usa --output-format json per catturare la risposta quando piped (bug #76) --- extensions/index.ts | 56 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/extensions/index.ts b/extensions/index.ts index 468f26e..84be003 100644 --- a/extensions/index.ts +++ b/extensions/index.ts @@ -104,14 +104,27 @@ async function runAgy( signal?: AbortSignal, ): Promise { const bin = findAgy(); + // --output-format json: agy non scrive nulla su stdout quando è piped/redirected + // (bug #76). Il formato json emette un oggetto con la risposta in .response. + const fullArgs = [...args, "--output-format", "json"]; try { - const { stdout, stderr } = await execFileAsync(bin, args, { + const { stdout, stderr } = await execFileAsync(bin, fullArgs, { timeout: timeoutMs, maxBuffer: 20 * 1024 * 1024, signal, env: { ...process.env, PATH: `${path.join(os.homedir(), ".local", "bin")}:${process.env.PATH ?? ""}` }, }); - return { output: stdout, error: stderr || null, exitCode: 0 }; + // estrai la risposta dal JSON + let output = stdout; + try { + const parsed = JSON.parse(stdout); + if (parsed && typeof parsed.response === "string") { + output = parsed.response; + } + } catch { + /* non-JSON: usa stdout grezzo */ + } + return { output, error: stderr || null, exitCode: 0 }; } catch (err: any) { const code = typeof err.code === "number" ? err.code : 1; return { @@ -269,6 +282,32 @@ function stopRecording(): Promise { }); } +// Ottimizza l'audio: taglia il silenzio iniziale/finale e converte in MP3 (più piccolo) +async function optimizeAudio(input: string): Promise { + const output = input.replace(/\.wav$/, ".mp3"); + try { + await execFileAsync( + "ffmpeg", + [ + "-y", + "-i", + input, + "-af", + "silenceremove=start_periods=1:start_threshold=-50dB:start_silence=0.5,areverse,silenceremove=start_periods=1:start_threshold=-50dB:start_silence=0.5,areverse", + "-c:a", + "libmp3lame", + "-q:a", + "4", + output, + ], + { timeout: 30_000 }, + ); + return output; + } catch { + return input; // fallback al file originale + } +} + function extractText(content: any): string { if (typeof content === "string") return content; if (Array.isArray(content)) { @@ -871,17 +910,22 @@ export default function agyExtension(pi: ExtensionAPI) { return; } - ctx.ui.notify("Registrazione fermata, trascrizione in corso...", "info"); + ctx.ui.notify("Registrazione fermata, ottimizzazione audio...", "info"); + const optimized = await optimizeAudio(file); + + ctx.ui.notify("Trascrizione in corso...", "info"); const context = getConversationContext(ctx); const res = await executeAgy({ prompt: - `Trascrivi e interpreta il file audio al percorso ${file}.` + + `Trascrivi fedelmente il contenuto del file audio al percorso ${optimized}.` + `\n\nContesto della conversazione:\n${context || "(nessuno)"}` + - `\n\nRestituisci la trascrizione fedele e una breve interpretazione.`, + `\n\nRestituisci SOLO la trascrizione testuale, senza commenti.`, mode: "analyze", stateless: true, - filePaths: [file], + filePaths: [optimized], yolo: true, + model: "Gemini 3.6 Flash (Medium)", + effort: "low", }); ctx.ui.setStatus("agy-rec", "");