@agentprojectcontext/apx 1.51.1 → 1.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,7 @@
18
18
  <link rel="apple-touch-icon" href="/favicon/dark/apple-touch-icon.png" media="(prefers-color-scheme: dark)" />
19
19
  <link rel="manifest" href="/favicon/white/site.webmanifest" media="(prefers-color-scheme: light)" />
20
20
  <link rel="manifest" href="/favicon/dark/site.webmanifest" media="(prefers-color-scheme: dark)" />
21
- <script type="module" crossorigin src="/assets/index-C1tm8nvc.js"></script>
21
+ <script type="module" crossorigin src="/assets/index-adeTv5sA.js"></script>
22
22
  <link rel="stylesheet" crossorigin href="/assets/index-L1pXYFUg.css">
23
23
  </head>
24
24
  <body class="bg-background text-foreground antialiased">
@@ -16,6 +16,7 @@ interface Props {
16
16
  engines: TtsEngineInfo[];
17
17
  order: string[]; // effective chain order from the daemon
18
18
  onToggleEnabled: (id: string, enabled: boolean) => void;
19
+ onToggleEmotions: (id: string, enabled: boolean) => void;
19
20
  onReorder: (nextOrder: string[]) => void;
20
21
  onConfigure: (id: string) => void;
21
22
  onRemove: (id: string) => void;
@@ -27,6 +28,7 @@ export function VoiceProviderList({
27
28
  engines,
28
29
  order,
29
30
  onToggleEnabled,
31
+ onToggleEmotions,
30
32
  onReorder,
31
33
  onConfigure,
32
34
  onRemove,
@@ -108,6 +110,23 @@ export function VoiceProviderList({
108
110
  </div>
109
111
 
110
112
  <div className="flex shrink-0 items-center gap-2">
113
+ {e.emotionsApplicable && (
114
+ <button
115
+ type="button"
116
+ onClick={() => onToggleEmotions(id, !e.emotionsOn)}
117
+ disabled={busy}
118
+ title={t("voice_ui.emotions_hint")}
119
+ data-testid={`voice-provider-${id}-emotions`}
120
+ className={cn(
121
+ "rounded-md border px-2 py-1 text-xs font-medium transition-colors disabled:opacity-50",
122
+ e.emotionsOn
123
+ ? "border-emerald-500/50 bg-emerald-500/10 text-emerald-300"
124
+ : "border-border text-muted-fg hover:text-fg",
125
+ )}
126
+ >
127
+ {t("voice_ui.emotions_short")}
128
+ </button>
129
+ )}
111
130
  <Switch
112
131
  checked={e.enabled}
113
132
  onChange={(v) => onToggleEnabled(id, v)}
@@ -1158,6 +1158,7 @@ export const en = {
1158
1158
  style_ph: "speak in a cheerful, energetic tone",
1159
1159
  temperature_label: "Temperature (optional)",
1160
1160
  temperature_hint: "Sampling temperature for custom endpoints. Empty = server default.",
1161
+ emotions_short: "Emotions",
1161
1162
  emotions_label: "Inline emotion tags",
1162
1163
  emotions_hint: "When this engine speaks, let the agent drop [happy]/[whisper]-style tags into voice replies to color the delivery. Only enable it if this engine understands the tags (e.g. a QVox/Qwen3-TTS endpoint) — otherwise they're stripped before synthesis.",
1163
1164
  emotions_tags_label: "Allowed tags",
@@ -1156,6 +1156,7 @@ export const es = {
1156
1156
  style_ph: "hablá en un tono alegre y enérgico",
1157
1157
  temperature_label: "Temperatura (opcional)",
1158
1158
  temperature_hint: "Temperatura de sampleo para endpoints custom. Vacío = default del servidor.",
1159
+ emotions_short: "Emociones",
1159
1160
  emotions_label: "Tags de emoción inline",
1160
1161
  emotions_hint: "Cuando hable este motor, deja que el agente meta tags tipo [happy]/[whisper] en las respuestas de voz para darles color. Activalo solo si este motor entiende los tags (ej. un endpoint QVox/Qwen3-TTS) — si no, se quitan antes de sintetizar.",
1161
1162
  emotions_tags_label: "Tags permitidos",
@@ -25,6 +25,9 @@ export interface TtsEngineInfo {
25
25
  custom?: boolean; // user-added OpenAI-compatible provider ("custom:<slug>")
26
26
  label?: string; // display name for custom providers
27
27
  note?: string; // e.g. the custom base_url
28
+ // UI-enriched (computed client-side from config, not reported by the daemon):
29
+ emotionsApplicable?: boolean; // engine can parse inline [tags] (custom/gemini)
30
+ emotionsOn?: boolean; // voice.tts.<...>.emotions.enabled
28
31
  }
29
32
 
30
33
  export interface TtsProvidersResponse {
@@ -40,9 +40,20 @@ export function VoiceScreen() {
40
40
  const transcriptionCfg = (cfgView.transcription || {}) as TranscriptionConfig;
41
41
  const configuredProvider = providersData?.configured_provider || voiceCfg.provider || "auto";
42
42
  const mode: TtsMode = providersData?.mode || voiceCfg.mode || "chain";
43
- const engines = providersData?.engines || [];
44
43
  const order = providersData?.order || [];
45
44
 
45
+ // Enrich the daemon's engine list with the emotion-tags state read straight
46
+ // from config (so the in-row toggle is live without a daemon round-trip).
47
+ // Only custom + Gemini parse inline [tags].
48
+ const engines = (providersData?.engines || []).map((e) => {
49
+ const applicable = !!e.custom || e.id === "gemini";
50
+ if (!applicable) return e;
51
+ const block = e.custom
52
+ ? voiceCfg.custom?.[e.id.slice(7)]
53
+ : voiceCfg.gemini;
54
+ return { ...e, emotionsApplicable: true, emotionsOn: !!block?.emotions?.enabled };
55
+ });
56
+
46
57
  const editingConfig = useMemo<Record<string, unknown>>(() => {
47
58
  if (!editing || editing === "__new__") return {};
48
59
  if (editing.startsWith("custom:")) {
@@ -67,6 +78,22 @@ export function VoiceScreen() {
67
78
  }
68
79
  };
69
80
 
81
+ const toggleEmotions = async (id: string, enabled: boolean) => {
82
+ setBusyDefault(true);
83
+ try {
84
+ const key = id.startsWith("custom:")
85
+ ? `voice.tts.custom.${id.slice(7)}.emotions.enabled`
86
+ : `voice.tts.${id}.emotions.enabled`;
87
+ await patch({ [key]: enabled });
88
+ await mutateProviders();
89
+ await mutateCfg();
90
+ } catch (e) {
91
+ toast.error((e as Error).message);
92
+ } finally {
93
+ setBusyDefault(false);
94
+ }
95
+ };
96
+
70
97
  const reorder = async (nextOrder: string[]) => {
71
98
  setBusyDefault(true);
72
99
  try {
@@ -130,6 +157,7 @@ export function VoiceScreen() {
130
157
  engines={engines}
131
158
  order={order}
132
159
  onToggleEnabled={toggleEnabled}
160
+ onToggleEmotions={toggleEmotions}
133
161
  onReorder={reorder}
134
162
  onConfigure={(id) => setEditing(id)}
135
163
  onRemove={removeCustom}