@danieltmn/tamnora 0.9.1 → 0.9.3
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/CHANGELOG.md +13 -0
- package/harness/packages/cli/src/tui/app.js +22 -3
- package/harness/packages/cli/src/tui/layout.js +59 -2
- package/harness/packages/cli/test/tui.test.js +30 -0
- package/harness/packages/core/src/config/app.js +2 -0
- package/harness/packages/core/src/index.js +2 -1
- package/harness/packages/core/src/providers/presets.js +4 -0
- package/harness/packages/core/src/stt/google.js +91 -0
- package/harness/packages/core/src/tools/diff.js +70 -1
- package/harness/packages/core/test/diff.test.js +24 -1
- package/harness/packages/server/public/app.js +824 -45
- package/harness/packages/server/public/index.html +121 -1
- package/harness/packages/server/public/styles.css +106 -12
- package/harness/packages/server/src/lib/channels/index.js +23 -3
- package/harness/packages/server/src/lib/channels/telegram.js +289 -101
- package/harness/packages/server/src/lib/presence.js +51 -0
- package/harness/packages/server/src/lib/profile.js +63 -0
- package/harness/packages/server/src/lib/prompt.js +7 -2
- package/harness/packages/server/src/lib/transcribe.js +51 -0
- package/harness/packages/server/src/routes/channels.js +2 -0
- package/harness/packages/server/src/routes/presence.js +17 -0
- package/harness/packages/server/src/routes/profile.js +22 -0
- package/harness/packages/server/src/routes/providers.js +2 -0
- package/harness/packages/server/src/routes/sessions.js +55 -33
- package/harness/packages/server/src/routes/transcribe.js +115 -0
- package/harness/packages/server/src/routes/tts.js +2 -1
- package/harness/packages/server/src/server.js +11 -0
- package/harness/packages/server/test/profile.test.js +85 -0
- package/harness/packages/server/test/server.test.js +34 -0
- package/harness/packages/server/test/telegram-features.test.js +241 -0
- package/harness/packages/server/test/telegram-voz.test.js +12 -5
- package/harness/packages/server/test/telegram.test.js +37 -2
- package/harness/packages/server/test/transcribe.test.js +215 -0
- package/harness/packages/server/test/tts.test.js +24 -0
- package/package.json +1 -1
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
</head>
|
|
35
35
|
<body>
|
|
36
36
|
<header class="topbar">
|
|
37
|
-
<div class="brand"><span class="logo"><span class="logo-t">T</span><span class="logo-h">H</span></span> <span class="ver">v0.9.
|
|
37
|
+
<div class="brand"><span class="logo"><span class="logo-t">T</span><span class="logo-h">H</span></span> <span class="ver">v0.9.3</span></div>
|
|
38
38
|
<nav class="tabs">
|
|
39
39
|
<button class="tab active" data-tab="chat">chat</button>
|
|
40
40
|
<button class="tab" data-tab="providers">proveedores</button>
|
|
@@ -141,6 +141,7 @@
|
|
|
141
141
|
<div id="toasts" class="toasts" aria-live="polite"></div>
|
|
142
142
|
<div id="attach-preview" class="attach-preview" hidden></div>
|
|
143
143
|
<div id="chat-queue" class="chat-queue" hidden></div>
|
|
144
|
+
<div id="audio-player" class="audio-player" hidden></div>
|
|
144
145
|
<div class="composer-row">
|
|
145
146
|
<span class="prompt-mark">❯</span>
|
|
146
147
|
<button type="button" id="attach-image" class="btn icon" title="Adjuntar imagen (o pegala / arrastrala)" aria-label="adjuntar imagen">
|
|
@@ -150,6 +151,14 @@
|
|
|
150
151
|
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
|
|
151
152
|
</svg>
|
|
152
153
|
</button>
|
|
154
|
+
<button type="button" id="mic-btn" class="btn icon" title="Dictar por voz (tocá para grabar y parar)" aria-label="dictar por voz">
|
|
155
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
156
|
+
<path d="M12 19v3" />
|
|
157
|
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
158
|
+
<path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" />
|
|
159
|
+
</svg>
|
|
160
|
+
<span class="mic-count" hidden></span>
|
|
161
|
+
</button>
|
|
153
162
|
<input id="image-input" type="file" accept="image/*" multiple hidden />
|
|
154
163
|
<textarea id="chat-input" rows="1" placeholder="escribí un mensaje…" title="Enter envía · Shift+Enter salto" spellcheck="false"></textarea>
|
|
155
164
|
<button class="btn primary" type="submit">enviar</button>
|
|
@@ -401,11 +410,13 @@
|
|
|
401
410
|
<div class="ajustes-wrap">
|
|
402
411
|
<nav class="ajustes-tabs" role="tablist" aria-label="secciones de ajustes">
|
|
403
412
|
<button type="button" class="ajustes-tab active" data-panel="agente" role="tab">Agente</button>
|
|
413
|
+
<button type="button" class="ajustes-tab" data-panel="perfil" role="tab">Perfil</button>
|
|
404
414
|
<button type="button" class="ajustes-tab" data-panel="sesiones" role="tab">Sesiones</button>
|
|
405
415
|
<button type="button" class="ajustes-tab" data-panel="mcp" role="tab">MCP</button>
|
|
406
416
|
<button type="button" class="ajustes-tab" data-panel="skills" role="tab">Skills</button>
|
|
407
417
|
<button type="button" class="ajustes-tab" data-panel="telegram" role="tab">Telegram</button>
|
|
408
418
|
<button type="button" class="ajustes-tab" data-panel="voz" role="tab">Voz</button>
|
|
419
|
+
<button type="button" class="ajustes-tab" data-panel="transcripcion" role="tab">Transcripción</button>
|
|
409
420
|
<button type="button" class="ajustes-tab" data-panel="limites" role="tab">Límites</button>
|
|
410
421
|
</nav>
|
|
411
422
|
|
|
@@ -435,6 +446,38 @@
|
|
|
435
446
|
</div>
|
|
436
447
|
</div>
|
|
437
448
|
|
|
449
|
+
<div class="panel ajustes-panel" data-panel="perfil">
|
|
450
|
+
<div class="row between">
|
|
451
|
+
<h2>Perfil</h2>
|
|
452
|
+
<span id="perfil-status" class="summary">cargando…</span>
|
|
453
|
+
</div>
|
|
454
|
+
<p class="muted verify-line">
|
|
455
|
+
Datos que el agente conoce de vos. Viven en <code>app.json</code> (la casa) y se
|
|
456
|
+
inyectan en el prompt de todas las corridas.
|
|
457
|
+
</p>
|
|
458
|
+
<label class="field"><span>cómo querés que te llame</span>
|
|
459
|
+
<input id="perfil-address" spellcheck="false" placeholder="Señor · Amo · Amigo · Compañero · Daniel…" />
|
|
460
|
+
</label>
|
|
461
|
+
<label class="field"><span>nombre</span>
|
|
462
|
+
<input id="perfil-name" spellcheck="false" placeholder="Daniel" />
|
|
463
|
+
</label>
|
|
464
|
+
<label class="field"><span>idioma preferido</span>
|
|
465
|
+
<input id="perfil-language" spellcheck="false" placeholder="es" />
|
|
466
|
+
</label>
|
|
467
|
+
<label class="field"><span>notas</span>
|
|
468
|
+
<textarea id="perfil-notes" rows="3" placeholder="Preferencias, contexto, cómo te gusta trabajar…"></textarea>
|
|
469
|
+
</label>
|
|
470
|
+
<div class="row between">
|
|
471
|
+
<span class="muted">datos extra</span>
|
|
472
|
+
<button type="button" id="perfil-add-field" class="btn">+ agregar</button>
|
|
473
|
+
</div>
|
|
474
|
+
<div id="perfil-fields" class="perfil-fields"></div>
|
|
475
|
+
<div class="row">
|
|
476
|
+
<button id="perfil-save" class="btn primary" type="button">guardar</button>
|
|
477
|
+
<span id="perfil-msg" class="summary"></span>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
|
|
438
481
|
<div class="panel ajustes-panel" data-panel="sesiones">
|
|
439
482
|
<div class="row between">
|
|
440
483
|
<h2>Sesiones</h2>
|
|
@@ -512,6 +555,27 @@
|
|
|
512
555
|
<label class="field"><span>modelo de transcripción (voz)</span>
|
|
513
556
|
<input id="tg-transcribe" spellcheck="false" placeholder="whisper-1" />
|
|
514
557
|
</label>
|
|
558
|
+
<label class="check" title="avisar y pedir permisos por Telegram de los proyectos abiertos en la consola">
|
|
559
|
+
<input id="tg-notify" type="checkbox" /> Avisar por Telegram (proyectos abiertos en la consola)
|
|
560
|
+
</label>
|
|
561
|
+
<div class="voz-announce">
|
|
562
|
+
<span class="muted">qué avisa (siempre con el nombre del proyecto):</span>
|
|
563
|
+
<label class="check" title="resumen al terminar un turno">
|
|
564
|
+
<input id="tg-notify-done" type="checkbox" /> el fin de turno
|
|
565
|
+
</label>
|
|
566
|
+
<label class="check" title="cuando un run falla">
|
|
567
|
+
<input id="tg-notify-error" type="checkbox" /> los errores
|
|
568
|
+
</label>
|
|
569
|
+
<label class="check" title="cuando el agente pide una aprobación">
|
|
570
|
+
<input id="tg-notify-permission" type="checkbox" /> los permisos
|
|
571
|
+
</label>
|
|
572
|
+
<label class="check" title="resultado de subagentes (tool task)">
|
|
573
|
+
<input id="tg-notify-agent" type="checkbox" /> los subagentes
|
|
574
|
+
</label>
|
|
575
|
+
</div>
|
|
576
|
+
<p class="muted verify-line">
|
|
577
|
+
Solo avisa de los proyectos que tengas <b>abiertos como pestañas</b> en la consola.
|
|
578
|
+
</p>
|
|
515
579
|
<details class="tg-help">
|
|
516
580
|
<summary>¿Cómo habilito mi chat?</summary>
|
|
517
581
|
<ol>
|
|
@@ -574,6 +638,62 @@
|
|
|
574
638
|
</div>
|
|
575
639
|
</div>
|
|
576
640
|
|
|
641
|
+
<div class="panel ajustes-panel" data-panel="transcripcion">
|
|
642
|
+
<div class="row between">
|
|
643
|
+
<h2>Transcripción</h2>
|
|
644
|
+
<span id="tr-status" class="summary">cargando…</span>
|
|
645
|
+
</div>
|
|
646
|
+
<p class="muted verify-line">
|
|
647
|
+
Audio → texto para el <b>dictado del chat</b> (micrófono del input) y las notas de voz
|
|
648
|
+
de Telegram.
|
|
649
|
+
</p>
|
|
650
|
+
<label class="field"><span>motor</span>
|
|
651
|
+
<select id="tr-mode" title="cómo se transcribe el dictado del chat">
|
|
652
|
+
<option value="server">Servidor · proveedor de transcripción</option>
|
|
653
|
+
<option value="google">Servidor · Google (sin API key)</option>
|
|
654
|
+
<option value="browser">Navegador · nativo (Web Speech API)</option>
|
|
655
|
+
</select>
|
|
656
|
+
</label>
|
|
657
|
+
<label class="field"><span>idioma del dictado (se le indica al motor para no confundirse)</span>
|
|
658
|
+
<input id="tr-language" spellcheck="false" placeholder="es-AR" />
|
|
659
|
+
</label>
|
|
660
|
+
<div id="tr-server-fields">
|
|
661
|
+
<label class="field"><span>proveedor de transcripción (vacío = proveedor activo)</span>
|
|
662
|
+
<input id="tr-provider" spellcheck="false" placeholder="groq · openai" />
|
|
663
|
+
</label>
|
|
664
|
+
<label class="field"><span>modelo</span>
|
|
665
|
+
<input id="tr-model" spellcheck="false" placeholder="whisper-large-v3-turbo" />
|
|
666
|
+
</label>
|
|
667
|
+
<p class="muted verify-line">
|
|
668
|
+
El proveedor activo no siempre sabe transcribir (p. ej. DeepSeek), así que conviene
|
|
669
|
+
apuntar a uno con <code>/audio/transcriptions</code> (Groq, OpenAI…). Si lo dejás
|
|
670
|
+
vacío se usa lo configurado en <b>Telegram</b> y, si tampoco hay, el proveedor activo
|
|
671
|
+
con <code>whisper-1</code>.
|
|
672
|
+
</p>
|
|
673
|
+
</div>
|
|
674
|
+
<p class="muted verify-line" id="tr-google-note" hidden>
|
|
675
|
+
Usa el servicio de voz de Chromium/Google <b>desde el servidor</b>, sin API key ni
|
|
676
|
+
depender del navegador (servicio no oficial, como el TTS de Edge). Configurá el idioma.
|
|
677
|
+
</p>
|
|
678
|
+
<p class="muted verify-line" id="tr-browser-note" hidden>
|
|
679
|
+
El navegador transcribe en vivo (Chrome/Edge) sin usar el servidor. Necesita el
|
|
680
|
+
servicio de voz de Google; en Brave o sin internet suele fallar (error «network»).
|
|
681
|
+
</p>
|
|
682
|
+
<div class="row">
|
|
683
|
+
<button id="tr-save" class="btn primary" type="button">guardar</button>
|
|
684
|
+
<button id="tr-test" class="btn icon" type="button" title="grabar y ver qué devuelve la transcripción" aria-label="probar transcripción">
|
|
685
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
686
|
+
<path d="M12 19v3" />
|
|
687
|
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
688
|
+
<path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" />
|
|
689
|
+
</svg>
|
|
690
|
+
<span class="mic-count" hidden></span>
|
|
691
|
+
</button>
|
|
692
|
+
<span id="tr-msg" class="summary"></span>
|
|
693
|
+
</div>
|
|
694
|
+
<p id="tr-test-result" class="summary"></p>
|
|
695
|
+
</div>
|
|
696
|
+
|
|
577
697
|
<div class="panel ajustes-panel" data-panel="limites">
|
|
578
698
|
<div class="row between">
|
|
579
699
|
<h2>Límites del agente</h2>
|
|
@@ -521,7 +521,7 @@ body.resizing * { cursor: col-resize !important; }
|
|
|
521
521
|
}
|
|
522
522
|
.msg-menu[hidden] { display: none; }
|
|
523
523
|
.msg-menu-sep { height: 1px; margin: 4px 6px; background: var(--border); }
|
|
524
|
-
.msg-
|
|
524
|
+
.msg-menu .msg-act {
|
|
525
525
|
width: 100%;
|
|
526
526
|
display: flex;
|
|
527
527
|
align-items: center;
|
|
@@ -537,11 +537,24 @@ body.resizing * { cursor: col-resize !important; }
|
|
|
537
537
|
}
|
|
538
538
|
.msg-act .mi { flex: 0 0 auto; color: var(--muted); }
|
|
539
539
|
.msg-act .mlabel { min-width: 0; }
|
|
540
|
-
.msg-
|
|
541
|
-
.msg-
|
|
542
|
-
.msg-
|
|
543
|
-
.msg-
|
|
544
|
-
.msg-
|
|
540
|
+
.msg-menu .msg-act:hover { background: var(--theirs); }
|
|
541
|
+
.msg-menu .msg-act:hover .mi { color: var(--accent); }
|
|
542
|
+
.msg-menu .msg-act.danger { color: var(--danger); }
|
|
543
|
+
.msg-menu .msg-act.danger:hover { background: var(--theirs); }
|
|
544
|
+
.msg-menu .msg-act.danger:hover .mi { color: var(--danger); }
|
|
545
|
+
|
|
546
|
+
/* En PC el menú del mensaje va en linea (una fila), con el mismo lenguaje
|
|
547
|
+
visual de la consola. En pantallas chicas queda como lista desplegable. */
|
|
548
|
+
@media (min-width: 821px) {
|
|
549
|
+
.msg-menu {
|
|
550
|
+
flex-direction: row;
|
|
551
|
+
align-items: center;
|
|
552
|
+
min-width: 0;
|
|
553
|
+
gap: 2px;
|
|
554
|
+
}
|
|
555
|
+
.msg-menu .msg-act { width: auto; white-space: nowrap; }
|
|
556
|
+
.msg-menu-sep { width: 1px; height: auto; align-self: stretch; margin: 6px 3px; }
|
|
557
|
+
}
|
|
545
558
|
.bubble .tools { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; font-weight: 700; }
|
|
546
559
|
|
|
547
560
|
.tools-actions { display: flex; align-items: center; gap: 8px; }
|
|
@@ -600,12 +613,73 @@ body.resizing * { cursor: col-resize !important; }
|
|
|
600
613
|
overflow: auto;
|
|
601
614
|
}
|
|
602
615
|
.tool-card > .tool-out:empty { display: none; }
|
|
603
|
-
.tool-card > .tool-audio {
|
|
604
|
-
display:
|
|
605
|
-
|
|
606
|
-
|
|
616
|
+
.tool-card > .tool-audio-btn {
|
|
617
|
+
display: inline-flex;
|
|
618
|
+
align-items: center;
|
|
619
|
+
gap: 6px;
|
|
607
620
|
margin: 4px 0 2px 18px;
|
|
621
|
+
padding: 4px 10px;
|
|
622
|
+
border: 1px solid var(--border);
|
|
623
|
+
border-radius: 999px;
|
|
624
|
+
background: transparent;
|
|
625
|
+
color: var(--accent);
|
|
626
|
+
cursor: pointer;
|
|
627
|
+
font: 600 12px var(--sans);
|
|
628
|
+
}
|
|
629
|
+
.tool-card > .tool-audio-btn:hover { background: var(--theirs); }
|
|
630
|
+
|
|
631
|
+
/* Reproductor único de audio (narración, speak, resumen): visible y detenible. */
|
|
632
|
+
.audio-player {
|
|
633
|
+
display: flex;
|
|
634
|
+
align-items: center;
|
|
635
|
+
gap: 10px;
|
|
636
|
+
padding: 7px 10px;
|
|
637
|
+
margin-bottom: 6px;
|
|
638
|
+
border: 1px solid var(--border);
|
|
639
|
+
border-radius: 10px;
|
|
640
|
+
background: var(--solid, var(--panel));
|
|
641
|
+
box-shadow: 0 6px 18px rgb(0 0 0 / 0.12);
|
|
642
|
+
}
|
|
643
|
+
.audio-player[hidden] { display: none; }
|
|
644
|
+
.audio-player .ap-btn {
|
|
645
|
+
flex: 0 0 auto;
|
|
646
|
+
display: inline-grid;
|
|
647
|
+
place-items: center;
|
|
648
|
+
width: 28px;
|
|
649
|
+
height: 28px;
|
|
650
|
+
padding: 0;
|
|
651
|
+
border: 1px solid var(--border);
|
|
652
|
+
border-radius: 999px;
|
|
653
|
+
background: transparent;
|
|
654
|
+
color: var(--accent);
|
|
655
|
+
cursor: pointer;
|
|
656
|
+
}
|
|
657
|
+
.audio-player .ap-btn:hover { background: var(--theirs); }
|
|
658
|
+
.audio-player .ap-stop { color: var(--danger); }
|
|
659
|
+
.audio-player .ap-info { flex: 1 1 auto; min-width: 0; }
|
|
660
|
+
.audio-player .ap-label {
|
|
661
|
+
display: block;
|
|
662
|
+
color: var(--muted);
|
|
663
|
+
font: 600 11px var(--sans);
|
|
664
|
+
text-transform: uppercase;
|
|
665
|
+
letter-spacing: 0.04em;
|
|
666
|
+
margin-bottom: 4px;
|
|
667
|
+
}
|
|
668
|
+
.audio-player .ap-progress {
|
|
669
|
+
height: 4px;
|
|
670
|
+
border-radius: 999px;
|
|
671
|
+
background: var(--border);
|
|
672
|
+
overflow: hidden;
|
|
608
673
|
}
|
|
674
|
+
.audio-player .ap-bar {
|
|
675
|
+
width: 0%;
|
|
676
|
+
height: 100%;
|
|
677
|
+
border-radius: 999px;
|
|
678
|
+
background: var(--accent);
|
|
679
|
+
transition: width 0.15s linear;
|
|
680
|
+
}
|
|
681
|
+
.audio-player .ap-queue { flex: 0 0 auto; color: var(--muted); font: 600 11px var(--sans); }
|
|
682
|
+
.audio-player .ap-queue[hidden] { display: none; }
|
|
609
683
|
.tool-more {
|
|
610
684
|
display: inline-block;
|
|
611
685
|
margin: 2px 0 2px 18px;
|
|
@@ -762,9 +836,28 @@ details.reasoning .body { color: var(--muted); white-space: pre-wrap; margin-top
|
|
|
762
836
|
place-items: center;
|
|
763
837
|
flex: 0 0 auto;
|
|
764
838
|
line-height: 1;
|
|
839
|
+
position: relative;
|
|
765
840
|
}
|
|
766
841
|
.btn.icon svg { width: 18px; height: 18px; display: block; }
|
|
767
842
|
.btn.icon:hover { color: var(--accent); }
|
|
843
|
+
/* Dictado activo: verde titilando (cuenta regresiva y grabación). */
|
|
844
|
+
.btn.icon.recording { color: #2eb872; border-color: currentColor; animation: live-pulse 1s ease-in-out infinite; }
|
|
845
|
+
.btn.icon.busy { opacity: 0.55; }
|
|
846
|
+
.btn.icon.mic-counting > svg { visibility: hidden; }
|
|
847
|
+
.mic-count {
|
|
848
|
+
position: absolute;
|
|
849
|
+
inset: 0;
|
|
850
|
+
display: grid;
|
|
851
|
+
place-items: center;
|
|
852
|
+
font: 700 13px var(--mono);
|
|
853
|
+
color: #2eb872;
|
|
854
|
+
}
|
|
855
|
+
.mic-count[hidden] { display: none; }
|
|
856
|
+
|
|
857
|
+
.perfil-fields { display: flex; flex-direction: column; gap: 6px; }
|
|
858
|
+
.perfil-field { display: flex; gap: 6px; align-items: center; }
|
|
859
|
+
.perfil-field .pf-key { flex: 0 0 34%; }
|
|
860
|
+
.perfil-field .pf-value { flex: 1; }
|
|
768
861
|
|
|
769
862
|
.chat-queue { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; border: 1px dashed var(--border); border-radius: 9px; background: var(--theirs); }
|
|
770
863
|
.chat-queue[hidden] { display: none; }
|
|
@@ -927,7 +1020,7 @@ pre.code code { background: none; border: 0; padding: 0; font-size: 0.92em; }
|
|
|
927
1020
|
left: 14px;
|
|
928
1021
|
right: 14px;
|
|
929
1022
|
bottom: calc(100% - 6px);
|
|
930
|
-
max-height:
|
|
1023
|
+
max-height: min(60vh, 420px);
|
|
931
1024
|
overflow-y: auto;
|
|
932
1025
|
display: flex;
|
|
933
1026
|
flex-direction: column;
|
|
@@ -935,8 +1028,9 @@ pre.code code { background: none; border: 0; padding: 0; font-size: 0.92em; }
|
|
|
935
1028
|
padding: 6px;
|
|
936
1029
|
border: 1px solid var(--border);
|
|
937
1030
|
border-radius: 12px;
|
|
938
|
-
background: var(--panel);
|
|
1031
|
+
background: var(--solid, var(--panel));
|
|
939
1032
|
box-shadow: 0 12px 32px rgb(0 0 0 / 0.22);
|
|
1033
|
+
backdrop-filter: blur(18px);
|
|
940
1034
|
z-index: 30;
|
|
941
1035
|
}
|
|
942
1036
|
.slash-menu[hidden] { display: none; }
|
|
@@ -19,6 +19,16 @@ function parseAllowed(raw) {
|
|
|
19
19
|
.filter(Boolean);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
const NOTIFY_EVENTS = ['done', 'error', 'permission', 'agent'];
|
|
23
|
+
|
|
24
|
+
/** Avisos por Telegram: interruptor global + que eventos llegan. */
|
|
25
|
+
export function sanitizeNotify(raw) {
|
|
26
|
+
const value = raw && typeof raw === 'object' ? raw : {};
|
|
27
|
+
const notify = { enabled: value.enabled === true };
|
|
28
|
+
for (const key of NOTIFY_EVENTS) notify[key] = value[key] !== false;
|
|
29
|
+
return notify;
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
/**
|
|
23
33
|
* Crea el manager de canales. `load()` arranca (o reinicia) los canales segun
|
|
24
34
|
* la config efectiva (app.json + tamnora.json + entorno), `status()` la resume
|
|
@@ -37,6 +47,7 @@ export function createChannelsManager(ctx, { log = () => {}, enabled = true } =
|
|
|
37
47
|
if (Array.isArray(cfg.allowedChatIds) && cfg.allowedChatIds.length) tg.allowedChatIds = cfg.allowedChatIds;
|
|
38
48
|
if (cfg.transcribeModel) tg.transcribeModel = cfg.transcribeModel;
|
|
39
49
|
if (cfg.transcribeProvider) tg.transcribeProvider = cfg.transcribeProvider;
|
|
50
|
+
if (cfg.notify) tg.notify = cfg.notify;
|
|
40
51
|
return tg;
|
|
41
52
|
};
|
|
42
53
|
|
|
@@ -49,6 +60,7 @@ export function createChannelsManager(ctx, { log = () => {}, enabled = true } =
|
|
|
49
60
|
allowedChatIds,
|
|
50
61
|
transcribeModel: tg.transcribeModel,
|
|
51
62
|
transcribeProvider: tg.transcribeProvider,
|
|
63
|
+
notify: sanitizeNotify(tg.notify),
|
|
52
64
|
};
|
|
53
65
|
}
|
|
54
66
|
|
|
@@ -62,9 +74,16 @@ export function createChannelsManager(ctx, { log = () => {}, enabled = true } =
|
|
|
62
74
|
function load() {
|
|
63
75
|
stop();
|
|
64
76
|
if (!enabled) return { enabled: false, channels: [] };
|
|
65
|
-
const { token, allowedChatIds, transcribeModel, transcribeProvider } = effective();
|
|
77
|
+
const { token, allowedChatIds, transcribeModel, transcribeProvider, notify } = effective();
|
|
66
78
|
const channels = [];
|
|
67
|
-
const telegram = startTelegramChannel(ctx, {
|
|
79
|
+
const telegram = startTelegramChannel(ctx, {
|
|
80
|
+
token,
|
|
81
|
+
allowedChatIds,
|
|
82
|
+
transcribeModel,
|
|
83
|
+
transcribeProvider,
|
|
84
|
+
notify,
|
|
85
|
+
log,
|
|
86
|
+
});
|
|
68
87
|
if (telegram) channels.push(telegram);
|
|
69
88
|
const unsubscribe = ctx.runs?.subscribeAll?.((sessionId, event) => {
|
|
70
89
|
for (const channel of channels) {
|
|
@@ -81,13 +100,14 @@ export function createChannelsManager(ctx, { log = () => {}, enabled = true } =
|
|
|
81
100
|
}
|
|
82
101
|
|
|
83
102
|
function status() {
|
|
84
|
-
const { token, allowedChatIds, transcribeModel, transcribeProvider } = effective();
|
|
103
|
+
const { token, allowedChatIds, transcribeModel, transcribeProvider, notify } = effective();
|
|
85
104
|
return {
|
|
86
105
|
tokenSet: Boolean(token),
|
|
87
106
|
tokenHint: tokenHint(token),
|
|
88
107
|
allowedChatIds,
|
|
89
108
|
transcribeModel: transcribeModel || 'whisper-1',
|
|
90
109
|
transcribeProvider: transcribeProvider || '',
|
|
110
|
+
notify,
|
|
91
111
|
running: Boolean(active && active.channels.length),
|
|
92
112
|
};
|
|
93
113
|
}
|