@agentprojectcontext/apx 1.51.0 → 1.51.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentprojectcontext/apx",
3
- "version": "1.51.0",
3
+ "version": "1.51.1",
4
4
  "description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -62,7 +62,10 @@ export function emotionConfigFor(globalConfig, providerId) {
62
62
  * the intent of selectTtsEngine without the async availability probes:
63
63
  * - explicit provider arg wins
64
64
  * - single mode → voice.tts.provider
65
- * - chain mode → first enabled engine with emotions on, else first enabled
65
+ * - chain mode → the FIRST enabled engine in order (what selectTtsEngine
66
+ * would speak with). We deliberately do NOT prefer an emotion-capable
67
+ * engine here: the guide must reflect the engine that will actually speak,
68
+ * otherwise the agent emits tags a different engine never asked for.
66
69
  */
67
70
  export function resolveSpeakingProvider(globalConfig, provider) {
68
71
  if (provider && provider !== "auto") return provider;
@@ -72,8 +75,7 @@ export function resolveSpeakingProvider(globalConfig, provider) {
72
75
  const order = resolveChainOrder(cfg).filter(
73
76
  (id) => id !== "mock" && enabledOf(cfg, id)
74
77
  );
75
- const withEmotion = order.find((id) => emotionConfigFor(globalConfig, id).enabled);
76
- return withEmotion || order[0] || cfg.provider || undefined;
78
+ return order[0] || cfg.provider || undefined;
77
79
  }
78
80
 
79
81
  /**
@@ -22,6 +22,7 @@ import fs from "node:fs";
22
22
  import path from "node:path";
23
23
  import { readConfig } from "#core/config/index.js";
24
24
  import { synthesize } from "#core/voice/tts.js";
25
+ import { stripEmotionTags } from "#core/voice/emotions.js";
25
26
  import { transcribe } from "#core/voice/transcription.js";
26
27
  import { decodeAudioInput } from "#core/voice/audio-decode.js";
27
28
  import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
@@ -174,6 +175,12 @@ export function register(app, { projects, plugins, registries }) {
174
175
  replyText = userText;
175
176
  }
176
177
 
178
+ // Emotion tags ([excited], [whisper], …) are a TTS-only signal: keep them
179
+ // for synthesis (engines that support them split on them), but strip them
180
+ // from everything the user reads — the chat bubble, history, RAG.
181
+ const audioText = replyText;
182
+ replyText = stripEmotionTags(replyText);
183
+
177
184
  // Persist the turn to the cross-channel store (feeds RAG index,
178
185
  // search_messages, and the "active threads" block). channelCtx.channel is
179
186
  // the resolved surface ("deck"/"desktop"). Best-effort.
@@ -190,7 +197,7 @@ export function register(app, { projects, plugins, registries }) {
190
197
  if (replyText) {
191
198
  try {
192
199
  tts = await synthesize({
193
- text: replyText,
200
+ text: audioText,
194
201
  voice: body.voice,
195
202
  language: body.language,
196
203
  provider: body.provider,