happy-imou-cloud 2.1.9 → 2.1.11

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.
Files changed (27) hide show
  1. package/dist/{BaseReasoningProcessor-CgEO6Vh-.mjs → BaseReasoningProcessor-BrQM8i44.mjs} +2 -2
  2. package/dist/{BaseReasoningProcessor-DV6TAtd7.cjs → BaseReasoningProcessor-G3hno_kB.cjs} +2 -2
  3. package/dist/{ProviderSelectionHandler-C6ILAmE3.cjs → ProviderSelectionHandler-B0HZZgMG.cjs} +2 -2
  4. package/dist/{ProviderSelectionHandler-dczuX21U.mjs → ProviderSelectionHandler-bkna8Dc0.mjs} +2 -2
  5. package/dist/{api-CfHYTLZX.mjs → api-CEUrdZHY.mjs} +246 -27
  6. package/dist/{api-CfmTDha2.cjs → api-CtQFwLUJ.cjs} +246 -27
  7. package/dist/{command-Bw2XKjA9.cjs → command-C6M6wHp1.cjs} +3 -3
  8. package/dist/{command-B0HfYWYW.mjs → command-DZ8Od7tC.mjs} +3 -3
  9. package/dist/{index-C3bSe5_d.cjs → index-AwnMQcsU.cjs} +171 -18
  10. package/dist/{index-BKjWLXkN.mjs → index-Drrrz-Ot.mjs} +169 -16
  11. package/dist/index.cjs +3 -3
  12. package/dist/index.mjs +3 -3
  13. package/dist/lib.cjs +1 -1
  14. package/dist/lib.d.cts +52 -39
  15. package/dist/lib.d.mts +52 -39
  16. package/dist/lib.mjs +1 -1
  17. package/dist/{persistence-Db4p7gsX.cjs → persistence-n4WMW33G.cjs} +1 -1
  18. package/dist/{persistence-Nun5c360.mjs → persistence-tRrZOs0P.mjs} +1 -1
  19. package/dist/{registerKillSessionHandler-FIQEW2Rx.cjs → registerKillSessionHandler-DKWTYJ3L.cjs} +3 -3
  20. package/dist/{registerKillSessionHandler-C1S9ytML.mjs → registerKillSessionHandler-DPh8LfUR.mjs} +3 -3
  21. package/dist/{runClaude-Dxye_oZf.cjs → runClaude-7rux3CCC.cjs} +5 -5
  22. package/dist/{runClaude-ClXU2LZA.mjs → runClaude-C-JjQB5i.mjs} +5 -5
  23. package/dist/{runCodex-IaL6Vqwc.cjs → runCodex-D81osbbB.cjs} +6 -6
  24. package/dist/{runCodex-CXu7_Wgk.mjs → runCodex-uHVY1g3W.mjs} +6 -6
  25. package/dist/{runGemini-BlJjRWN1.mjs → runGemini-DFhltfvZ.mjs} +5 -5
  26. package/dist/{runGemini-BgcZcZsv.cjs → runGemini-DaSpiomM.cjs} +5 -5
  27. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { a as createSessionMetadata, p as publishSessionRegistration } from './index-BKjWLXkN.mjs';
2
- import { s as startOfflineReconnection, c as configuration, i as isAuthenticationRequiredError, l as logger } from './api-CfHYTLZX.mjs';
1
+ import { a as createSessionMetadata, p as publishSessionRegistration } from './index-Drrrz-Ot.mjs';
2
+ import { s as startOfflineReconnection, c as configuration, i as isAuthenticationRequiredError, l as logger } from './api-CEUrdZHY.mjs';
3
3
  import { EventEmitter } from 'node:events';
4
4
  import { randomUUID } from 'node:crypto';
5
5
 
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-C3bSe5_d.cjs');
4
- var api = require('./api-CfmTDha2.cjs');
3
+ var index = require('./index-AwnMQcsU.cjs');
4
+ var api = require('./api-CtQFwLUJ.cjs');
5
5
  var node_events = require('node:events');
6
6
  var node_crypto = require('node:crypto');
7
7
 
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var api = require('./api-CfmTDha2.cjs');
4
- var registerKillSessionHandler = require('./registerKillSessionHandler-FIQEW2Rx.cjs');
3
+ var api = require('./api-CtQFwLUJ.cjs');
4
+ var registerKillSessionHandler = require('./registerKillSessionHandler-DKWTYJ3L.cjs');
5
5
 
6
6
  async function runModeLoop(opts) {
7
7
  let currentMode = opts.startingMode;
@@ -1,5 +1,5 @@
1
- import { l as logger } from './api-CfHYTLZX.mjs';
2
- import { g as getPendingInteractionTimeoutMs, I as INTERACTION_SUPERSEDED_ERROR, a as INTERACTION_TIMED_OUT_ERROR } from './registerKillSessionHandler-C1S9ytML.mjs';
1
+ import { l as logger } from './api-CEUrdZHY.mjs';
2
+ import { g as getPendingInteractionTimeoutMs, I as INTERACTION_SUPERSEDED_ERROR, a as INTERACTION_TIMED_OUT_ERROR } from './registerKillSessionHandler-DPh8LfUR.mjs';
3
3
 
4
4
  async function runModeLoop(opts) {
5
5
  let currentMode = opts.startingMode;
@@ -16,7 +16,7 @@ import { spawn } from 'node:child_process';
16
16
  import { Expo } from 'expo-server-sdk';
17
17
 
18
18
  var name = "happy-imou-cloud";
19
- var version = "2.1.9";
19
+ var version = "2.1.11";
20
20
  var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
21
21
  var author = "long.zhu";
22
22
  var license = "MIT";
@@ -431,7 +431,7 @@ async function listDaemonLogFiles(limit = 50) {
431
431
  return { file, path: fullPath, modified: stats.mtime };
432
432
  }).sort((a, b) => b.modified.getTime() - a.modified.getTime());
433
433
  try {
434
- const { readDaemonState } = await import('./persistence-Nun5c360.mjs');
434
+ const { readDaemonState } = await import('./persistence-tRrZOs0P.mjs');
435
435
  const state = await readDaemonState();
436
436
  if (!state) {
437
437
  return logs;
@@ -1737,6 +1737,8 @@ const MAX_PENDING_RELIABLE_SESSION_MESSAGE_BYTES = 512 * 1024;
1737
1737
  const PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT = 150;
1738
1738
  const PROTOCOL_V3_CHANGES_PAGE_LIMIT = 200;
1739
1739
  const PROTOCOL_V3_CAPABILITIES_WAIT_MS = 250;
1740
+ const COMMITTED_SESSION_WRITE_ACK_TIMEOUT_MS = 5e3;
1741
+ const RELIABLE_SESSION_MESSAGE_RETRY_DELAY_MS = 250;
1740
1742
  class ApiSessionClient extends EventEmitter {
1741
1743
  credentials;
1742
1744
  sessionId;
@@ -1752,8 +1754,12 @@ class ApiSessionClient extends EventEmitter {
1752
1754
  metadataLock = new AsyncLock();
1753
1755
  encryptionKey;
1754
1756
  encryptionVariant;
1755
- pendingReliableSessionMessages = [];
1756
- pendingReliableSessionMessageBytes = 0;
1757
+ queuedReliableSessionMessages = [];
1758
+ queuedReliableSessionMessageBytes = 0;
1759
+ inFlightReliableSessionMessage = null;
1760
+ reliableSessionMessageFlushInProgress = false;
1761
+ reliableSessionMessageDrainWaiters = [];
1762
+ reliableSessionMessageRetryTimer = null;
1757
1763
  reconnectAfterServerDisconnectTimer = null;
1758
1764
  lastSocketServerError = null;
1759
1765
  protocolV3SessionSync = null;
@@ -1765,6 +1771,7 @@ class ApiSessionClient extends EventEmitter {
1765
1771
  protocolV3Descriptor = null;
1766
1772
  protocolV3SocketCapabilities = null;
1767
1773
  protocolV3SessionSyncWaitTimer = null;
1774
+ committedSessionWriteAckMode;
1768
1775
  constructor(credentials, session) {
1769
1776
  super();
1770
1777
  this.credentials = credentials;
@@ -1776,6 +1783,7 @@ class ApiSessionClient extends EventEmitter {
1776
1783
  this.encryptionKey = session.encryptionKey;
1777
1784
  this.encryptionVariant = session.encryptionVariant;
1778
1785
  this.lastChangeSeq = session.seq;
1786
+ this.committedSessionWriteAckMode = this.credentials.signing ? "unknown" : "unsupported";
1779
1787
  this.rpcHandlerManager = new RpcHandlerManager({
1780
1788
  scopePrefix: this.sessionId,
1781
1789
  encryptionKey: this.encryptionKey,
@@ -1808,11 +1816,13 @@ class ApiSessionClient extends EventEmitter {
1808
1816
  this.socket.on("connect", () => {
1809
1817
  logger.debug("Socket connected successfully");
1810
1818
  this.clearReconnectAfterServerDisconnectTimer();
1819
+ this.clearReliableSessionMessageRetryTimer();
1811
1820
  this.lastSocketServerError = null;
1812
1821
  this.rpcHandlerManager.onSocketConnect(this.socket);
1813
- this.flushReliableSessionMessages();
1814
1822
  this.protocolV3Descriptor = null;
1815
1823
  this.protocolV3SocketCapabilities = null;
1824
+ this.committedSessionWriteAckMode = this.credentials.signing ? "unknown" : "unsupported";
1825
+ this.flushReliableSessionMessages();
1816
1826
  this.scheduleProtocolV3SessionSync();
1817
1827
  });
1818
1828
  this.socket.on("rpc-request", async (data, callback) => {
@@ -1820,8 +1830,10 @@ class ApiSessionClient extends EventEmitter {
1820
1830
  });
1821
1831
  this.socket.on("disconnect", (reason) => {
1822
1832
  logger.debug("[API] Socket disconnected:", reason);
1833
+ this.clearReliableSessionMessageRetryTimer();
1823
1834
  this.rpcHandlerManager.onSocketDisconnect();
1824
1835
  this.invalidateProtocolV3SessionSync();
1836
+ this.resolveReliableSessionMessageDrainWaitersIfIdle();
1825
1837
  this.retryAfterServerDisconnect(reason);
1826
1838
  });
1827
1839
  this.socket.on("connect_error", (error) => {
@@ -1849,8 +1861,10 @@ class ApiSessionClient extends EventEmitter {
1849
1861
  connectionType: data.connectionType,
1850
1862
  capabilities: data.protocol.capabilities
1851
1863
  });
1864
+ this.committedSessionWriteAckMode = this.supportsKnownProtocolCapability("message_ack_v3") ? "supported" : "unsupported";
1852
1865
  this.clearProtocolV3SessionSyncWaitTimer();
1853
1866
  this.scheduleProtocolV3SessionSync({ waitForCapabilities: false });
1867
+ this.flushReliableSessionMessages();
1854
1868
  } catch (error) {
1855
1869
  logger.debug("[SOCKET] [CAPABILITIES] [ERROR] Error handling capabilities event", { error });
1856
1870
  }
@@ -1990,15 +2004,24 @@ class ApiSessionClient extends EventEmitter {
1990
2004
  };
1991
2005
  const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
1992
2006
  const eventType = typeof body?.type === "string" ? body.type : "unknown";
2007
+ const reliableMessage = {
2008
+ encrypted,
2009
+ type: `codex:${eventType}`,
2010
+ localId: randomUUID()
2011
+ };
1993
2012
  if (!this.socket.connected) {
1994
2013
  if (this.shouldBufferReliableCodexMessage(body)) {
1995
2014
  logger.debug("[API] Socket not connected, buffering reliable Codex message:", { type: eventType });
1996
- this.bufferReliableSessionMessage({ encrypted, type: `codex:${eventType}` });
2015
+ this.enqueueReliableSessionMessage(reliableMessage);
1997
2016
  } else {
1998
2017
  logger.debug("[API] Socket not connected, dropping non-critical Codex message:", { type: eventType });
1999
2018
  }
2000
2019
  return;
2001
2020
  }
2021
+ if (this.shouldBufferReliableCodexMessage(body) && this.shouldUseCommittedSessionWriteAcks()) {
2022
+ this.enqueueReliableSessionMessage(reliableMessage);
2023
+ return;
2024
+ }
2002
2025
  this.emitEncryptedSessionMessage(encrypted);
2003
2026
  }
2004
2027
  /**
@@ -2023,13 +2046,18 @@ class ApiSessionClient extends EventEmitter {
2023
2046
  logger.debug(`[SOCKET] Sending ACP message from ${provider}:`, { type: body.type, hasMessage: "message" in body });
2024
2047
  const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
2025
2048
  const eventType = typeof body?.type === "string" ? body.type : "unknown";
2049
+ const reliableMessage = {
2050
+ encrypted,
2051
+ type: `acp:${provider}:${eventType}`,
2052
+ localId: randomUUID()
2053
+ };
2026
2054
  if (!this.socket.connected) {
2027
2055
  if (this.shouldBufferReliableAcpMessage(body)) {
2028
2056
  logger.debug("[API] Socket not connected, buffering reliable ACP message:", {
2029
2057
  provider,
2030
2058
  type: eventType
2031
2059
  });
2032
- this.bufferReliableSessionMessage({ encrypted, type: `acp:${provider}:${eventType}` });
2060
+ this.enqueueReliableSessionMessage(reliableMessage);
2033
2061
  } else {
2034
2062
  logger.debug("[API] Socket not connected, dropping non-critical ACP message:", {
2035
2063
  provider,
@@ -2038,10 +2066,14 @@ class ApiSessionClient extends EventEmitter {
2038
2066
  }
2039
2067
  return;
2040
2068
  }
2069
+ if (this.shouldBufferReliableAcpMessage(body) && this.shouldUseCommittedSessionWriteAcks()) {
2070
+ this.enqueueReliableSessionMessage(reliableMessage);
2071
+ return;
2072
+ }
2041
2073
  this.emitEncryptedSessionMessage(encrypted);
2042
2074
  }
2043
2075
  sendSessionEvent(event, id) {
2044
- let content = {
2076
+ const content = {
2045
2077
  role: "agent",
2046
2078
  content: {
2047
2079
  id: id ?? randomUUID(),
@@ -2050,15 +2082,24 @@ class ApiSessionClient extends EventEmitter {
2050
2082
  }
2051
2083
  };
2052
2084
  const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
2085
+ const reliableMessage = {
2086
+ encrypted,
2087
+ type: `event:${event.type}`,
2088
+ localId: content.content.id
2089
+ };
2053
2090
  if (!this.socket.connected) {
2054
2091
  if (this.shouldBufferReliableSessionEvent(event)) {
2055
2092
  logger.debug("[API] Socket not connected, buffering session event:", { type: event.type });
2056
- this.bufferReliableSessionMessage({ encrypted, type: `event:${event.type}` });
2093
+ this.enqueueReliableSessionMessage(reliableMessage);
2057
2094
  } else {
2058
2095
  logger.debug("[API] Socket not connected, dropping session event:", { type: event.type });
2059
2096
  }
2060
2097
  return;
2061
2098
  }
2099
+ if (this.shouldBufferReliableSessionEvent(event) && this.shouldUseCommittedSessionWriteAcks()) {
2100
+ this.enqueueReliableSessionMessage(reliableMessage);
2101
+ return;
2102
+ }
2062
2103
  this.emitEncryptedSessionMessage(encrypted);
2063
2104
  }
2064
2105
  /**
@@ -2172,6 +2213,10 @@ class ApiSessionClient extends EventEmitter {
2172
2213
  * Wait for socket buffer to flush
2173
2214
  */
2174
2215
  async flush() {
2216
+ if (!this.socket.connected) {
2217
+ return;
2218
+ }
2219
+ await this.waitForReliableSessionMessagesToDrain();
2175
2220
  if (!this.socket.connected) {
2176
2221
  return;
2177
2222
  }
@@ -2461,31 +2506,197 @@ class ApiSessionClient extends EventEmitter {
2461
2506
  supportsKnownProtocolCapability(capability) {
2462
2507
  return Array.isArray(this.protocolV3Descriptor?.capabilities) && this.protocolV3Descriptor.capabilities.includes(capability);
2463
2508
  }
2464
- emitEncryptedSessionMessage(encrypted) {
2509
+ shouldUseCommittedSessionWriteAcks() {
2510
+ if (!this.credentials.signing) {
2511
+ return false;
2512
+ }
2513
+ return this.committedSessionWriteAckMode !== "unsupported";
2514
+ }
2515
+ emitEncryptedSessionMessage(encrypted, localId) {
2465
2516
  this.socket.emit("message", {
2466
2517
  sid: this.sessionId,
2467
- message: encrypted
2518
+ message: encrypted,
2519
+ ...typeof localId === "string" ? { localId } : {}
2468
2520
  });
2469
2521
  }
2470
- bufferReliableSessionMessage(message) {
2471
- this.pendingReliableSessionMessages.push(message);
2472
- this.pendingReliableSessionMessageBytes += message.encrypted.length;
2522
+ enqueueReliableSessionMessage(message) {
2523
+ this.queuedReliableSessionMessages.push(message);
2524
+ this.queuedReliableSessionMessageBytes += message.encrypted.length;
2473
2525
  this.trimPendingReliableSessionMessages();
2526
+ this.flushReliableSessionMessages();
2527
+ }
2528
+ dequeueReliableSessionMessage() {
2529
+ const message = this.queuedReliableSessionMessages.shift() ?? null;
2530
+ if (!message) {
2531
+ return null;
2532
+ }
2533
+ this.queuedReliableSessionMessageBytes -= message.encrypted.length;
2534
+ return message;
2474
2535
  }
2475
2536
  flushReliableSessionMessages() {
2476
- if (!this.socket.connected || this.pendingReliableSessionMessages.length === 0) {
2537
+ this.clearReliableSessionMessageRetryTimer();
2538
+ if (this.reliableSessionMessageFlushInProgress || !this.socket.connected) {
2539
+ this.resolveReliableSessionMessageDrainWaitersIfIdle();
2540
+ return;
2541
+ }
2542
+ this.reliableSessionMessageFlushInProgress = true;
2543
+ void (async () => {
2544
+ try {
2545
+ while (this.socket.connected) {
2546
+ const message = this.inFlightReliableSessionMessage ?? this.dequeueReliableSessionMessage();
2547
+ if (!message) {
2548
+ this.inFlightReliableSessionMessage = null;
2549
+ break;
2550
+ }
2551
+ if (this.inFlightReliableSessionMessage === null) {
2552
+ logger.debug("[API] Flushing buffered session message after reconnect", {
2553
+ type: message.type,
2554
+ localId: message.localId
2555
+ });
2556
+ }
2557
+ this.inFlightReliableSessionMessage = message;
2558
+ if (this.shouldUseCommittedSessionWriteAcks()) {
2559
+ const result = await this.emitEncryptedSessionMessageWithAck(message);
2560
+ if (result === "retry") {
2561
+ this.scheduleReliableSessionMessageRetry();
2562
+ break;
2563
+ }
2564
+ } else {
2565
+ this.emitEncryptedSessionMessage(message.encrypted, message.localId);
2566
+ }
2567
+ this.inFlightReliableSessionMessage = null;
2568
+ }
2569
+ } finally {
2570
+ this.reliableSessionMessageFlushInProgress = false;
2571
+ this.resolveReliableSessionMessageDrainWaitersIfIdle();
2572
+ }
2573
+ })();
2574
+ }
2575
+ async waitForReliableSessionMessagesToDrain() {
2576
+ if (!this.socket.connected) {
2577
+ return;
2578
+ }
2579
+ this.flushReliableSessionMessages();
2580
+ if (!this.reliableSessionMessageFlushInProgress && this.inFlightReliableSessionMessage === null && this.queuedReliableSessionMessages.length === 0) {
2477
2581
  return;
2478
2582
  }
2479
- const buffered = this.pendingReliableSessionMessages.splice(0, this.pendingReliableSessionMessages.length);
2480
- this.pendingReliableSessionMessageBytes = 0;
2481
- logger.debug("[API] Flushing buffered session messages after reconnect", {
2482
- count: buffered.length,
2483
- types: buffered.map((message) => message.type)
2583
+ await new Promise((resolve) => {
2584
+ this.reliableSessionMessageDrainWaiters.push(resolve);
2484
2585
  });
2485
- for (const message of buffered) {
2486
- this.emitEncryptedSessionMessage(message.encrypted);
2586
+ }
2587
+ resolveReliableSessionMessageDrainWaitersIfIdle() {
2588
+ if (!this.socket.connected) {
2589
+ const disconnectedWaiters = this.reliableSessionMessageDrainWaiters.splice(0, this.reliableSessionMessageDrainWaiters.length);
2590
+ for (const waiter of disconnectedWaiters) {
2591
+ waiter();
2592
+ }
2593
+ return;
2594
+ }
2595
+ if (this.reliableSessionMessageFlushInProgress || this.inFlightReliableSessionMessage !== null || this.queuedReliableSessionMessages.length > 0) {
2596
+ return;
2597
+ }
2598
+ const waiters = this.reliableSessionMessageDrainWaiters.splice(0, this.reliableSessionMessageDrainWaiters.length);
2599
+ for (const waiter of waiters) {
2600
+ waiter();
2487
2601
  }
2488
2602
  }
2603
+ scheduleReliableSessionMessageRetry() {
2604
+ if (this.reliableSessionMessageRetryTimer || !this.socket.connected || this.inFlightReliableSessionMessage === null) {
2605
+ return;
2606
+ }
2607
+ this.reliableSessionMessageRetryTimer = setTimeout(() => {
2608
+ this.reliableSessionMessageRetryTimer = null;
2609
+ if (!this.socket.connected || this.inFlightReliableSessionMessage === null) {
2610
+ this.resolveReliableSessionMessageDrainWaitersIfIdle();
2611
+ return;
2612
+ }
2613
+ this.flushReliableSessionMessages();
2614
+ }, RELIABLE_SESSION_MESSAGE_RETRY_DELAY_MS);
2615
+ }
2616
+ clearReliableSessionMessageRetryTimer() {
2617
+ if (!this.reliableSessionMessageRetryTimer) {
2618
+ return;
2619
+ }
2620
+ clearTimeout(this.reliableSessionMessageRetryTimer);
2621
+ this.reliableSessionMessageRetryTimer = null;
2622
+ }
2623
+ async emitEncryptedSessionMessageWithAck(message) {
2624
+ try {
2625
+ const response = await this.withAckTimeout(
2626
+ Promise.resolve(this.socket.emitWithAck("message", {
2627
+ sid: this.sessionId,
2628
+ message: message.encrypted,
2629
+ localId: message.localId
2630
+ })),
2631
+ COMMITTED_SESSION_WRITE_ACK_TIMEOUT_MS
2632
+ );
2633
+ if (!response || typeof response !== "object" || !("ok" in response)) {
2634
+ if (this.committedSessionWriteAckMode === "unknown") {
2635
+ logger.debug("[API] Falling back to fire-and-forget session writes because websocket write acknowledgements are unavailable", {
2636
+ sessionId: this.sessionId,
2637
+ type: message.type
2638
+ });
2639
+ this.committedSessionWriteAckMode = "unsupported";
2640
+ this.emitEncryptedSessionMessage(message.encrypted, message.localId);
2641
+ return "committed";
2642
+ }
2643
+ logger.debug("[API] Session write acknowledgement payload was invalid, will retry on reconnect", {
2644
+ sessionId: this.sessionId,
2645
+ type: message.type,
2646
+ response
2647
+ });
2648
+ return "retry";
2649
+ }
2650
+ const ackResponse = response;
2651
+ if (!ackResponse.ok) {
2652
+ logger.debug("[API] Session write was rejected by the server", {
2653
+ sessionId: this.sessionId,
2654
+ type: message.type,
2655
+ localId: message.localId,
2656
+ error: ackResponse.error
2657
+ });
2658
+ this.emit("message-ack", ackResponse);
2659
+ return "committed";
2660
+ }
2661
+ this.committedSessionWriteAckMode = "supported";
2662
+ this.emit("message-ack", ackResponse);
2663
+ return "committed";
2664
+ } catch (error) {
2665
+ if (this.committedSessionWriteAckMode === "unknown" && isAckTimeoutError(error)) {
2666
+ logger.debug("[API] Websocket write acknowledgements timed out before protocol capabilities were confirmed, falling back to fire-and-forget writes", {
2667
+ sessionId: this.sessionId,
2668
+ type: message.type
2669
+ });
2670
+ this.committedSessionWriteAckMode = "unsupported";
2671
+ this.emitEncryptedSessionMessage(message.encrypted, message.localId);
2672
+ return "committed";
2673
+ }
2674
+ logger.debug("[API] Reliable session write acknowledgement failed, will retry when the socket reconnects", {
2675
+ sessionId: this.sessionId,
2676
+ type: message.type,
2677
+ localId: message.localId,
2678
+ error
2679
+ });
2680
+ return "retry";
2681
+ }
2682
+ }
2683
+ async withAckTimeout(promise, timeoutMs) {
2684
+ return await new Promise((resolve, reject) => {
2685
+ const timeout = setTimeout(() => {
2686
+ reject(createAckTimeoutError(timeoutMs));
2687
+ }, timeoutMs);
2688
+ promise.then(
2689
+ (value) => {
2690
+ clearTimeout(timeout);
2691
+ resolve(value);
2692
+ },
2693
+ (error) => {
2694
+ clearTimeout(timeout);
2695
+ reject(error);
2696
+ }
2697
+ );
2698
+ });
2699
+ }
2489
2700
  shouldBufferReliableCodexMessage(body) {
2490
2701
  switch (body?.type) {
2491
2702
  case "message":
@@ -2532,19 +2743,19 @@ class ApiSessionClient extends EventEmitter {
2532
2743
  }
2533
2744
  trimPendingReliableSessionMessages() {
2534
2745
  let dropped = 0;
2535
- while (this.pendingReliableSessionMessages.length > MAX_PENDING_RELIABLE_SESSION_MESSAGES || this.pendingReliableSessionMessageBytes > MAX_PENDING_RELIABLE_SESSION_MESSAGE_BYTES) {
2536
- const removed = this.pendingReliableSessionMessages.shift();
2746
+ while (this.queuedReliableSessionMessages.length > MAX_PENDING_RELIABLE_SESSION_MESSAGES || this.queuedReliableSessionMessageBytes > MAX_PENDING_RELIABLE_SESSION_MESSAGE_BYTES) {
2747
+ const removed = this.queuedReliableSessionMessages.shift();
2537
2748
  if (!removed) {
2538
2749
  break;
2539
2750
  }
2540
- this.pendingReliableSessionMessageBytes -= removed.encrypted.length;
2751
+ this.queuedReliableSessionMessageBytes -= removed.encrypted.length;
2541
2752
  dropped += 1;
2542
2753
  }
2543
2754
  if (dropped > 0) {
2544
2755
  logger.debug("[API] Dropped oldest buffered session messages to cap reconnect memory usage", {
2545
2756
  dropped,
2546
- remaining: this.pendingReliableSessionMessages.length,
2547
- bytes: this.pendingReliableSessionMessageBytes
2757
+ remaining: this.queuedReliableSessionMessages.length,
2758
+ bytes: this.queuedReliableSessionMessageBytes
2548
2759
  });
2549
2760
  }
2550
2761
  }
@@ -2601,6 +2812,14 @@ function createAbortError() {
2601
2812
  error.name = "AbortError";
2602
2813
  return error;
2603
2814
  }
2815
+ function createAckTimeoutError(timeoutMs) {
2816
+ const error = new Error(`Timed out waiting for websocket message acknowledgement after ${timeoutMs}ms`);
2817
+ error.name = "AckTimeoutError";
2818
+ return error;
2819
+ }
2820
+ function isAckTimeoutError(error) {
2821
+ return error instanceof Error && error.name === "AckTimeoutError";
2822
+ }
2604
2823
 
2605
2824
  class ApiMachineClient {
2606
2825
  constructor(credentials, machine) {