@agentprojectcontext/apx 1.53.0 → 1.53.2

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.53.0",
3
+ "version": "1.53.2",
4
4
  "description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -211,6 +211,37 @@ function sanitizeHeaders(h) {
211
211
  return out;
212
212
  }
213
213
 
214
+ function redactHeaderValue(key, value) {
215
+ const k = String(key || "").toLowerCase();
216
+ if (
217
+ k === "authorization" ||
218
+ k === "proxy-authorization" ||
219
+ k.includes("token") ||
220
+ k.includes("secret") ||
221
+ k.includes("key")
222
+ ) {
223
+ return "[redacted]";
224
+ }
225
+ const s = String(value ?? "");
226
+ return s.length > 96 ? `${s.slice(0, 24)}...${s.slice(-12)}` : s;
227
+ }
228
+
229
+ function redactHeaders(headers) {
230
+ const out = {};
231
+ for (const [k, v] of Object.entries(headers || {})) {
232
+ out[k] = redactHeaderValue(k, v);
233
+ }
234
+ return out;
235
+ }
236
+
237
+ function summarizeRpcBody(method, params) {
238
+ return JSON.stringify({
239
+ jsonrpc: "2.0",
240
+ method,
241
+ params_keys: params && typeof params === "object" ? Object.keys(params) : [],
242
+ });
243
+ }
244
+
214
245
  class HttpMcpClient {
215
246
  constructor({ name, url, headers = {} }) {
216
247
  this.name = name;
@@ -220,6 +251,7 @@ class HttpMcpClient {
220
251
  this._nextId = 1;
221
252
  this._initialized = false;
222
253
  this._initPromise = null;
254
+ this.sessionId = null;
223
255
  this.logs = [];
224
256
  this.startedAt = null;
225
257
  this.lastError = null;
@@ -230,23 +262,32 @@ class HttpMcpClient {
230
262
  if (this.logs.length > LOG_CAP) this.logs.shift();
231
263
  }
232
264
 
265
+ _requestHeaders({ accept = "application/json, text/event-stream" } = {}) {
266
+ return {
267
+ "Content-Type": "application/json",
268
+ Accept: accept,
269
+ "MCP-Protocol-Version": "2024-11-05",
270
+ ...(this.sessionId ? { "Mcp-Session-Id": this.sessionId } : {}),
271
+ ...this.headers,
272
+ };
273
+ }
274
+
233
275
  async _rpc(method, params, timeoutMs = DEFAULT_TIMEOUT_MS) {
234
276
  if (!this.startedAt) this.startedAt = nowIso();
235
277
  const id = this._nextId++;
236
278
  const body = JSON.stringify({ jsonrpc: "2.0", id, method, params });
237
279
  const ctrl = new AbortController();
238
280
  const timer = setTimeout(() => ctrl.abort(), timeoutMs);
239
- this._log("info", `POST ${method}`);
281
+ const headers = this._requestHeaders();
282
+ this._log(
283
+ "info",
284
+ `POST ${method} headers=${JSON.stringify(redactHeaders(headers))} body=${summarizeRpcBody(method, params)}`
285
+ );
240
286
  let res;
241
287
  try {
242
288
  res = await fetch(this.url, {
243
289
  method: "POST",
244
- headers: {
245
- "Content-Type": "application/json",
246
- Accept: "application/json, text/event-stream",
247
- "MCP-Protocol-Version": "2024-11-05",
248
- ...this.headers,
249
- },
290
+ headers,
250
291
  body,
251
292
  signal: ctrl.signal,
252
293
  });
@@ -258,6 +299,11 @@ class HttpMcpClient {
258
299
  clearTimeout(timer);
259
300
  }
260
301
  const contentType = res.headers.get("content-type") || "";
302
+ const sessionId = res.headers.get("mcp-session-id");
303
+ if (sessionId) {
304
+ this.sessionId = sessionId;
305
+ this._log("info", `session ${sessionId}`);
306
+ }
261
307
  const text = await res.text();
262
308
  if (!res.ok) {
263
309
  this.lastError = `HTTP ${res.status}`;
@@ -306,14 +352,14 @@ class HttpMcpClient {
306
352
  );
307
353
  // Best-effort notification — many servers ignore this for HTTP.
308
354
  try {
355
+ const headers = this._requestHeaders({ accept: "application/json" });
356
+ this._log(
357
+ "info",
358
+ `POST notifications/initialized headers=${JSON.stringify(redactHeaders(headers))} body=${summarizeRpcBody("notifications/initialized", {})}`
359
+ );
309
360
  await fetch(this.url, {
310
361
  method: "POST",
311
- headers: {
312
- "Content-Type": "application/json",
313
- Accept: "application/json",
314
- "MCP-Protocol-Version": "2024-11-05",
315
- ...this.headers,
316
- },
362
+ headers,
317
363
  body: JSON.stringify({
318
364
  jsonrpc: "2.0",
319
365
  method: "notifications/initialized",
@@ -349,6 +395,7 @@ class HttpMcpClient {
349
395
  stop() {
350
396
  this._initialized = false;
351
397
  this._initPromise = null;
398
+ this.sessionId = null;
352
399
  }
353
400
  }
354
401
 
@@ -24,6 +24,7 @@ import {
24
24
  } from "../../desktop-ws.js";
25
25
  import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
26
26
  import { appendGlobalMessage } from "#core/stores/messages.js";
27
+ import { stripEmotionTags } from "#core/voice/emotions.js";
27
28
  import { CHANNELS } from "#core/constants/channels.js";
28
29
  import { tryResolveSkillCommand } from "#core/agent/skills/trigger.js";
29
30
 
@@ -120,7 +121,9 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
120
121
  if (!seg || seg === lastSegText) return;
121
122
  lastSegText = seg;
122
123
  emittedSegments.push(seg);
123
- _send(ws, { type: "segment", seq: ++segSeq, text: seg });
124
+ // `text` is what the bubble shows (no [tags]); `speak` keeps the inline
125
+ // emotion tags so the renderer's per-segment TTS can use them.
126
+ _send(ws, { type: "segment", seq: ++segSeq, text: stripEmotionTags(seg), speak: seg });
124
127
  };
125
128
 
126
129
  try {
@@ -178,7 +181,9 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
178
181
  // the closing segment (deduped against the last one).
179
182
  emitSegment((result.text || "").trim() || liveBuf.trim());
180
183
 
181
- const finalText = emittedSegments.join("\n\n");
184
+ // Emotion tags are a TTS-only signal — strip them from the text we display,
185
+ // persist, and feed back as conversation context.
186
+ const finalText = stripEmotionTags(emittedSegments.join("\n\n"));
182
187
  log(`desktop: super-agent turn done in ${Date.now() - t0}ms segments=${segSeq} text_len=${finalText.length} tools=${toolsExecuted.length}`);
183
188
 
184
189
  // Turn end. `segments` lets the renderer know how many bubbles to expect.
@@ -1187,8 +1187,10 @@
1187
1187
  appendTurn(m, true);
1188
1188
  queueRegisterSegment(m);
1189
1189
  // Synthesize THIS segment; tts-ready(seg=id) attaches its audio + queues
1190
- // it for gapless sequential playback.
1191
- window.apx?.requestTts?.(text, id);
1190
+ // it for gapless sequential playback. `speak` carries the inline emotion
1191
+ // tags (the bubble `text` has them stripped) so a tag-aware engine like
1192
+ // QVox can act on them; falls back to the visible text.
1193
+ window.apx?.requestTts?.(msg.speak || text, id);
1192
1194
  requestWindowResize();
1193
1195
  scrollConvToBottom();
1194
1196
  break;
@@ -2236,9 +2236,9 @@
2236
2236
  }
2237
2237
  },
2238
2238
  "node_modules/caniuse-lite": {
2239
- "version": "1.0.30001799",
2240
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
2241
- "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
2239
+ "version": "1.0.30001800",
2240
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz",
2241
+ "integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==",
2242
2242
  "dev": true,
2243
2243
  "funding": [
2244
2244
  {
@@ -2598,9 +2598,9 @@
2598
2598
  "license": "MIT"
2599
2599
  },
2600
2600
  "node_modules/electron-to-chromium": {
2601
- "version": "1.5.381",
2602
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.381.tgz",
2603
- "integrity": "sha512-n9Wa6yB+vDsGuA8AKbl/0z7HbvWqt5jxIdvr1IUicd0ryPrk7/xzwqLv8D9AbbvZ6avVNtXYLTfmgFHkwkyelg==",
2601
+ "version": "1.5.382",
2602
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.382.tgz",
2603
+ "integrity": "sha512-8ETaWbV6SZOrno+G93Ffd9ENsMtetqdnqj4nlfxFW90Sm5GgnuV28Kf62hqQVD6VUgzm7qFQKsTsAPmeUiU3Ug==",
2604
2604
  "dev": true,
2605
2605
  "license": "ISC"
2606
2606
  },
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ esbuild: true