@yoooclaw/phone-notifications 1.11.2-beta.4 → 1.11.4-beta.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.
package/dist/index.cjs CHANGED
@@ -299,6 +299,14 @@ var init_env = __esm({
299
299
  init_credentials();
300
300
  init_host();
301
301
  ENV_CONFIG = {
302
+ development: {
303
+ lightApiUrl: "https://openclaw-service-dev.yoooclaw.com/api/message/tob/sendMessage",
304
+ relayTunnelUrl: "wss://openclaw-service-dev.yoooclaw.com/message/messages/ws/plugin",
305
+ appNameMapUrl: "https://openclaw-service-dev.yoooclaw.com/api/application-config/app-package/config-all",
306
+ modelProxyLongRecordingSubmitTaskUrl: "https://openclaw-service-dev.yoooclaw.com/api/model-proxy/long-recording/submit-task",
307
+ modelProxyLongRecordingQueryTaskResultBaseUrl: "https://openclaw-service-dev.yoooclaw.com/api/model-proxy/long-recording/query-task-result",
308
+ accountFileDeleteUrl: "https://openclaw-service-dev.yoooclaw.com/api/account/file/delete"
309
+ },
302
310
  test: {
303
311
  lightApiUrl: "https://openclaw-service-test.yoooclaw.com/api/message/tob/sendMessage",
304
312
  relayTunnelUrl: "wss://openclaw-service-test.yoooclaw.com/message/messages/ws/plugin",
@@ -5472,7 +5480,7 @@ function readBuildInjectedVersion() {
5472
5480
  if (false) {
5473
5481
  return void 0;
5474
5482
  }
5475
- const version = "1.11.2-beta.4".trim();
5483
+ const version = "1.11.4-beta.0".trim();
5476
5484
  return version || void 0;
5477
5485
  }
5478
5486
  function readPluginVersionFromPackageJson() {
@@ -7343,7 +7351,7 @@ function resolveUpdateChannel(params) {
7343
7351
  if (params.currentVersion.includes("-")) {
7344
7352
  return "beta";
7345
7353
  }
7346
- return params.envName === "test" ? "beta" : "latest";
7354
+ return params.envName === "production" ? "latest" : "beta";
7347
7355
  }
7348
7356
 
7349
7357
  // src/update/index.ts
@@ -11994,6 +12002,7 @@ var WsProxy = class {
11994
12002
  // src/tunnel/proxy.ts
11995
12003
  var RELAY_TUNNEL_GATEWAY_CLIENT_INSTANCE_ID = "phone-notifications-relay-tunnel";
11996
12004
  var MAX_AUTO_PAIRING_APPROVALS = 3;
12005
+ var RECENT_ABORTED_CHAT_RUN_TTL_MS = 6e4;
11997
12006
  var approveDevicePairingPromise = null;
11998
12007
  var approveDevicePairingWarned = false;
11999
12008
  function formatErrorMessage(err2) {
@@ -12049,6 +12058,10 @@ var TunnelProxy = class {
12049
12058
  gatewayWsAutoPairingApprovals = 0;
12050
12059
  /** 等待 Gateway WS 握手完成后发送的帧队列 */
12051
12060
  gatewayWsPending = [];
12061
+ /** 记录已转发到 Gateway 的 req 元信息,便于回包时做定向处理。 */
12062
+ gatewayReqMetaById = /* @__PURE__ */ new Map();
12063
+ /** 最近由用户手动中断的 chat.run,用于过滤 runtime 误补发的 synthetic failure。 */
12064
+ recentAbortedChatRuns = /* @__PURE__ */ new Map();
12052
12065
  /** 设备身份,用于 Gateway connect 握手 */
12053
12066
  deviceIdentity;
12054
12067
  stateDir;
@@ -12102,6 +12115,8 @@ var TunnelProxy = class {
12102
12115
  this.gatewayWsReconnectRequested = false;
12103
12116
  this.gatewayWsAutoPairingApprovals = 0;
12104
12117
  this.gatewayWsPending = [];
12118
+ this.gatewayReqMetaById.clear();
12119
+ this.recentAbortedChatRuns.clear();
12105
12120
  }
12106
12121
  // ─── Gateway RPC WebSocket ───
12107
12122
  pushGatewayPending(payload, reason) {
@@ -12122,6 +12137,10 @@ var TunnelProxy = class {
12122
12137
  /** 处理 Relay 转发的 Gateway RPC 请求帧,原样通过 WebSocket 发给本地 Gateway */
12123
12138
  handleReqFrame(frame) {
12124
12139
  const payload = JSON.stringify(frame);
12140
+ this.gatewayReqMetaById.set(frame.id, {
12141
+ method: frame.method,
12142
+ sessionKey: this.normalizeSessionKey(frame.params?.sessionKey)
12143
+ });
12125
12144
  this.opts.logger.info(
12126
12145
  `TunnelProxy: req id=${frame.id} method=${frame.method} \u2192 gateway WS (${payload.length} chars)`
12127
12146
  );
@@ -12241,6 +12260,116 @@ var TunnelProxy = class {
12241
12260
  }
12242
12261
  }
12243
12262
  // ─── Gateway RPC WebSocket lifecycle ───
12263
+ normalizeSessionKey(value) {
12264
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
12265
+ }
12266
+ normalizeRunId(value) {
12267
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
12268
+ }
12269
+ pruneRecentAbortedChatRuns(now = Date.now()) {
12270
+ for (const [runId, entry] of this.recentAbortedChatRuns) {
12271
+ if (now - entry.abortedAtMs > RECENT_ABORTED_CHAT_RUN_TTL_MS) {
12272
+ this.recentAbortedChatRuns.delete(runId);
12273
+ }
12274
+ }
12275
+ }
12276
+ rememberRecentAbortedChatRun(runId, sessionKey) {
12277
+ const normalizedRunId = this.normalizeRunId(runId);
12278
+ const normalizedSessionKey = this.normalizeSessionKey(sessionKey);
12279
+ if (!normalizedRunId || !normalizedSessionKey) return;
12280
+ this.pruneRecentAbortedChatRuns();
12281
+ this.recentAbortedChatRuns.set(normalizedRunId, {
12282
+ sessionKey: normalizedSessionKey,
12283
+ abortedAtMs: Date.now()
12284
+ });
12285
+ }
12286
+ getAssistantMessageText(message) {
12287
+ if (!message || typeof message !== "object") return "";
12288
+ if (typeof message.text === "string") return message.text;
12289
+ if (!Array.isArray(message.content)) return "";
12290
+ return message.content.filter(
12291
+ (item) => item && typeof item === "object" && item.type === "text" && typeof item.text === "string"
12292
+ ).map((item) => item.text).join("");
12293
+ }
12294
+ looksLikeSyntheticAbortFailureText(text) {
12295
+ const normalized = text.trim().toLowerCase();
12296
+ if (!normalized) return false;
12297
+ return normalized.includes("agent failed before reply:") && normalized.includes("aborted") && normalized.includes("openclaw logs --follow");
12298
+ }
12299
+ isSyntheticAbortHistoryMessage(message, sessionKey) {
12300
+ const text = this.getAssistantMessageText(message);
12301
+ if (!this.looksLikeSyntheticAbortFailureText(text)) return false;
12302
+ const messageTimestamp = typeof message?.timestamp === "number" ? message.timestamp : null;
12303
+ const now = Date.now();
12304
+ this.pruneRecentAbortedChatRuns(now);
12305
+ for (const entry of this.recentAbortedChatRuns.values()) {
12306
+ if (entry.sessionKey !== sessionKey) continue;
12307
+ if (messageTimestamp === null) return true;
12308
+ if (Math.abs(messageTimestamp - entry.abortedAtMs) <= RECENT_ABORTED_CHAT_RUN_TTL_MS) {
12309
+ return true;
12310
+ }
12311
+ }
12312
+ return false;
12313
+ }
12314
+ maybeRewriteGatewayFrame(text, frame) {
12315
+ this.pruneRecentAbortedChatRuns();
12316
+ if (frame?.type === "event" && frame?.event === "chat" && frame?.payload?.state === "aborted") {
12317
+ this.rememberRecentAbortedChatRun(
12318
+ frame.payload?.runId,
12319
+ frame.payload?.sessionKey
12320
+ );
12321
+ return text;
12322
+ }
12323
+ if (frame?.type === "ack" && frame?.ok === false && typeof frame?.id === "string") {
12324
+ this.gatewayReqMetaById.delete(frame.id);
12325
+ return text;
12326
+ }
12327
+ let reqMeta;
12328
+ if (frame?.type === "res" && typeof frame?.id === "string") {
12329
+ reqMeta = this.gatewayReqMetaById.get(frame.id);
12330
+ this.gatewayReqMetaById.delete(frame.id);
12331
+ }
12332
+ if (frame?.type === "res" && frame?.ok === true && reqMeta?.method === "chat.abort") {
12333
+ const runIds = Array.isArray(frame?.payload?.runIds) ? frame.payload.runIds : [];
12334
+ for (const runId of runIds) {
12335
+ this.rememberRecentAbortedChatRun(runId, reqMeta.sessionKey);
12336
+ }
12337
+ return text;
12338
+ }
12339
+ if (frame?.type === "event" && frame?.event === "chat" && frame?.payload?.state === "final") {
12340
+ const runId = this.normalizeRunId(frame?.payload?.runId);
12341
+ const assistantText = this.getAssistantMessageText(frame?.payload?.message);
12342
+ const recentAbort = runId ? this.recentAbortedChatRuns.get(runId) : void 0;
12343
+ if (recentAbort && this.looksLikeSyntheticAbortFailureText(assistantText)) {
12344
+ this.opts.logger.info(
12345
+ `TunnelProxy: suppressing synthetic abort failure final for runId=${runId}`
12346
+ );
12347
+ return null;
12348
+ }
12349
+ return text;
12350
+ }
12351
+ if (frame?.type === "res" && frame?.ok === true && reqMeta?.method === "chat.history" && Array.isArray(frame?.payload?.messages)) {
12352
+ const sessionKey = this.normalizeSessionKey(frame?.payload?.sessionKey) ?? reqMeta.sessionKey;
12353
+ if (!sessionKey) return text;
12354
+ const filteredMessages = frame.payload.messages.filter(
12355
+ (message) => !this.isSyntheticAbortHistoryMessage(message, sessionKey)
12356
+ );
12357
+ if (filteredMessages.length === frame.payload.messages.length) {
12358
+ return text;
12359
+ }
12360
+ this.opts.logger.info(
12361
+ `TunnelProxy: removed ${frame.payload.messages.length - filteredMessages.length} synthetic abort history message(s) for session=${sessionKey}`
12362
+ );
12363
+ return JSON.stringify({
12364
+ ...frame,
12365
+ payload: {
12366
+ ...frame.payload,
12367
+ messages: filteredMessages
12368
+ }
12369
+ });
12370
+ }
12371
+ return text;
12372
+ }
12244
12373
  /** 确保到本地 Gateway 的 RPC WebSocket 已连接且握手完成 */
12245
12374
  ensureGatewayWs() {
12246
12375
  if (this.gatewayWsReady && this.gatewayWs?.readyState === wrapper_default.OPEN)
@@ -12325,7 +12454,10 @@ var TunnelProxy = class {
12325
12454
  ws.close();
12326
12455
  return;
12327
12456
  }
12328
- this.opts.client.sendRaw(text);
12457
+ const rewritten = this.maybeRewriteGatewayFrame(text, frame);
12458
+ if (rewritten !== null) {
12459
+ this.opts.client.sendRaw(rewritten);
12460
+ }
12329
12461
  });
12330
12462
  ws.on("close", (code, reason) => {
12331
12463
  const wasReady = this.gatewayWsReady;