@moxt-ai/mobius 0.0.295 → 0.0.296

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.
@@ -3,7 +3,7 @@ import { WebSocket } from "undici";
3
3
  import type { SessionEventSink } from "../../session/api/session-api.ts";
4
4
  import type { Timer } from "../../util/timer.ts";
5
5
  export declare const SESSION_EVENT_BATCH_WINDOW_MILLISECONDS = 100;
6
- /** Live delivery is best-effort. Recovery snapshots are owned by the daemon's local Session cache. */
6
+ /** Keeps unsent output in bounded memory across reconnects. Sent events are never replayed. */
7
7
  export declare class RuntimeSessionEventPublisher implements SessionEventSink {
8
8
  #private;
9
9
  constructor(timer: Timer);
@@ -16,7 +16,7 @@ export declare class RuntimeSessionEventPublisher implements SessionEventSink {
16
16
  readonly publishInfo: (event: SessionInfoUpdatedEvent) => void;
17
17
  readonly publishClosed: (event: SessionCloseNoticeEvent) => void;
18
18
  readonly publish: (event: SessionLiveEvent) => void;
19
- private readonly dropPendingEvents;
19
+ private readonly cancelFlush;
20
20
  private readonly flushEvents;
21
21
  }
22
22
  //# sourceMappingURL=session-event-publisher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-event-publisher.d.ts","sourceRoot":"","sources":["../../../src/runtime/relay/session-event-publisher.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EACjC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAiB,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAQhE,eAAO,MAAM,uCAAuC,MAAM,CAAC;AAE3D,sGAAsG;AACtG,qBAAa,4BAA6B,YAAW,gBAAgB;;IAOjE,YAAY,KAAK,EAAE,KAAK,EAEvB;IAED,QAAQ,CAAC,OAAO,WAAY,SAAS,KAAG,IAAI,CAG1C;IAEF,QAAQ,CAAC,UAAU,WAAY,SAAS,KAAG,IAAI,CAK7C;IAEF,QAAQ,CAAC,KAAK,QAAO,IAAI,CAGvB;IAEF,QAAQ,CAAC,KAAK,QAAO,IAAI,CAIvB;IAEF,QAAQ,CAAC,uBAAuB,cAAe,MAAM,aAAa,MAAM,QAAQ,MAAM,WAAW,MAAM,KAAG,IAAI,CAE5G;IACF,QAAQ,CAAC,aAAa,UAAW,yBAAyB,KAAG,IAAI,CAAsB;IACvF,QAAQ,CAAC,WAAW,UAAW,uBAAuB,KAAG,IAAI,CAAsB;IACnF,QAAQ,CAAC,aAAa,UAAW,uBAAuB,KAAG,IAAI,CAAsB;IAmBrF,QAAQ,CAAC,OAAO,UAAW,gBAAgB,KAAG,IAAI,CA+BhD;IAEF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAKhC;IAEF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAmB1B;CACL"}
1
+ {"version":3,"file":"session-event-publisher.d.ts","sourceRoot":"","sources":["../../../src/runtime/relay/session-event-publisher.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EACjC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAiB,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAShE,eAAO,MAAM,uCAAuC,MAAM,CAAC;AAY3D,+FAA+F;AAC/F,qBAAa,4BAA6B,YAAW,gBAAgB;;IAQjE,YAAY,KAAK,EAAE,KAAK,EAEvB;IAED,QAAQ,CAAC,OAAO,WAAY,SAAS,KAAG,IAAI,CAO1C;IAEF,QAAQ,CAAC,UAAU,WAAY,SAAS,KAAG,IAAI,CAK7C;IAEF,QAAQ,CAAC,KAAK,QAAO,IAAI,CAKvB;IAEF,QAAQ,CAAC,KAAK,QAAO,IAAI,CAIvB;IAEF,QAAQ,CAAC,uBAAuB,cAAe,MAAM,aAAa,MAAM,QAAQ,MAAM,WAAW,MAAM,KAAG,IAAI,CAE5G;IACF,QAAQ,CAAC,aAAa,UAAW,yBAAyB,KAAG,IAAI,CAAsB;IACvF,QAAQ,CAAC,WAAW,UAAW,uBAAuB,KAAG,IAAI,CAAsB;IACnF,QAAQ,CAAC,aAAa,UAAW,uBAAuB,KAAG,IAAI,CAAsB;IAoBrF,QAAQ,CAAC,OAAO,UAAW,gBAAgB,KAAG,IAAI,CAwDhD;IAEF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAG1B;IAEF,OAAO,CAAC,QAAQ,CAAC,WAAW,CA2B1B;CACL"}
@@ -1,39 +1,47 @@
1
1
  import { encodeDaemonToServiceMessage, SESSION_EVENT_BATCH_MAX_SIZE, SESSION_EVENT_KIND, } from "@moxt-ai/mobius-sdk/protocol";
2
2
  import { WebSocket } from "undici";
3
3
  const MAX_BUFFERED_BYTES = 32 * 1024 * 1024;
4
+ const MAX_PENDING_BYTES = 32 * 1024 * 1024;
4
5
  const MAX_EVENT_BATCH_BYTES = 1024 * 1024;
5
6
  const MAX_FRAME_BYTES = 8 * 1024 * 1024;
6
7
  // Browser-compatible WebSocket clients may initiate only 1000 or application close codes.
7
8
  const LOCAL_BACKPRESSURE_CLOSE_CODE = 4009;
8
9
  const EMPTY_ENVELOPE_BYTES = Buffer.byteLength(JSON.stringify({ events: [], kind: SESSION_EVENT_KIND }));
9
10
  export const SESSION_EVENT_BATCH_WINDOW_MILLISECONDS = 100;
10
- /** Live delivery is best-effort. Recovery snapshots are owned by the daemon's local Session cache. */
11
+ /** Keeps unsent output in bounded memory across reconnects. Sent events are never replayed. */
11
12
  export class RuntimeSessionEventPublisher {
12
- #pendingEvents = [];
13
- #pendingBytes = EMPTY_ENVELOPE_BYTES;
13
+ #pending = [];
14
+ #pendingBytes = 0;
15
+ #overflowReported = false;
14
16
  #pendingTask = null;
15
- #socket = null;
17
+ #connection = { kind: "disconnected" };
16
18
  #timer;
17
19
  constructor(timer) {
18
20
  this.#timer = timer;
19
21
  }
20
22
  connect = (socket) => {
21
- this.dropPendingEvents();
22
- this.#socket = socket;
23
+ if (this.#connection.kind === "closed") {
24
+ return;
25
+ }
26
+ this.#connection = { kind: "connected", socket };
27
+ // Hand off the unsent tail before a new command can publish newer events.
28
+ this.flushEvents(socket);
23
29
  };
24
30
  disconnect = (socket) => {
25
- if (this.#socket === socket) {
26
- this.#socket = null;
27
- this.dropPendingEvents();
31
+ if (this.#connection.kind === "connected" && this.#connection.socket === socket) {
32
+ this.#connection = { kind: "disconnected" };
33
+ this.cancelFlush();
28
34
  }
29
35
  };
30
36
  close = () => {
31
- this.#socket = null;
32
- this.dropPendingEvents();
37
+ this.#connection = { kind: "closed" };
38
+ this.cancelFlush();
39
+ this.#pending.length = 0;
40
+ this.#pendingBytes = 0;
33
41
  };
34
42
  flush = () => {
35
- if (this.#socket !== null) {
36
- this.flushEvents(this.#socket);
43
+ if (this.#connection.kind === "connected") {
44
+ this.flushEvents(this.#connection.socket);
37
45
  }
38
46
  };
39
47
  publishOperationFailure = (sessionId, requestId, code, message) => {
@@ -43,10 +51,11 @@ export class RuntimeSessionEventPublisher {
43
51
  publishInfo = (event) => this.#send(event);
44
52
  publishClosed = (event) => this.#send(event);
45
53
  #send = (message) => {
46
- const socket = this.#socket;
47
- if (socket === null || socket.readyState !== WebSocket.OPEN) {
54
+ const connection = this.#connection;
55
+ if (connection.kind !== "connected" || connection.socket.readyState !== WebSocket.OPEN) {
48
56
  return;
49
57
  }
58
+ const socket = connection.socket;
50
59
  const encoded = encodeDaemonToServiceMessage(message);
51
60
  if (socket.bufferedAmount + Buffer.byteLength(encoded, "utf8") > MAX_BUFFERED_BYTES) {
52
61
  socket.close(LOCAL_BACKPRESSURE_CLOSE_CODE, "Live event transport overloaded");
@@ -60,8 +69,7 @@ export class RuntimeSessionEventPublisher {
60
69
  }
61
70
  };
62
71
  publish = (event) => {
63
- const socket = this.#socket;
64
- if (socket === null || socket.readyState !== WebSocket.OPEN) {
72
+ if (this.#connection.kind === "closed") {
65
73
  return;
66
74
  }
67
75
  const bytes = Buffer.byteLength(JSON.stringify(event), "utf8") + 1;
@@ -74,45 +82,74 @@ export class RuntimeSessionEventPublisher {
74
82
  });
75
83
  return;
76
84
  }
77
- if (this.#pendingEvents.length > 0 && this.#pendingBytes + bytes > MAX_EVENT_BATCH_BYTES) {
78
- this.flushEvents(socket);
79
- if (socket.readyState !== WebSocket.OPEN) {
80
- return;
85
+ let batch = this.#pending.at(-1);
86
+ const newBatch = batch === undefined ||
87
+ batch.events.length >= SESSION_EVENT_BATCH_MAX_SIZE ||
88
+ batch.bytes + bytes > MAX_EVENT_BATCH_BYTES;
89
+ const additionalBytes = bytes + (newBatch ? EMPTY_ENVELOPE_BYTES : 0);
90
+ if (this.#pendingBytes + additionalBytes > MAX_PENDING_BYTES) {
91
+ if (!this.#overflowReported) {
92
+ this.#overflowReported = true;
93
+ console.warn("Live Session output exceeded the pending buffer limit", {
94
+ code: "live_event_buffer_full",
95
+ sessionId: event.sessionId,
96
+ eventId: event.eventId,
97
+ maximumBytes: MAX_PENDING_BYTES,
98
+ });
81
99
  }
100
+ return;
101
+ }
102
+ if (batch === undefined || newBatch) {
103
+ batch = { events: [], bytes: EMPTY_ENVELOPE_BYTES };
104
+ this.#pending.push(batch);
82
105
  }
83
- this.#pendingEvents.push(event);
84
- this.#pendingBytes += bytes;
85
- if (this.#pendingEvents.length >= SESSION_EVENT_BATCH_MAX_SIZE || this.#pendingBytes >= MAX_EVENT_BATCH_BYTES) {
106
+ batch.events.push(event);
107
+ batch.bytes += bytes;
108
+ this.#pendingBytes += additionalBytes;
109
+ const connection = this.#connection;
110
+ if (connection.kind !== "connected" || connection.socket.readyState !== WebSocket.OPEN) {
111
+ return;
112
+ }
113
+ const socket = connection.socket;
114
+ if (this.#pending.length > 1 ||
115
+ batch.events.length >= SESSION_EVENT_BATCH_MAX_SIZE ||
116
+ batch.bytes >= MAX_EVENT_BATCH_BYTES) {
86
117
  this.flushEvents(socket);
87
118
  return;
88
119
  }
89
120
  this.#pendingTask ??= this.#timer.schedule(() => this.flushEvents(socket), SESSION_EVENT_BATCH_WINDOW_MILLISECONDS);
90
121
  };
91
- dropPendingEvents = () => {
122
+ cancelFlush = () => {
92
123
  this.#pendingTask?.cancel();
93
124
  this.#pendingTask = null;
94
- this.#pendingEvents = [];
95
- this.#pendingBytes = EMPTY_ENVELOPE_BYTES;
96
125
  };
97
126
  flushEvents = (socket) => {
98
- this.#pendingTask?.cancel();
99
- this.#pendingTask = null;
100
- const events = this.#pendingEvents;
101
- this.#pendingEvents = [];
102
- this.#pendingBytes = EMPTY_ENVELOPE_BYTES;
103
- if (events.length === 0 || this.#socket !== socket || socket.readyState !== WebSocket.OPEN) {
127
+ this.cancelFlush();
128
+ const connection = this.#connection;
129
+ if (connection.kind !== "connected" || connection.socket !== socket || socket.readyState !== WebSocket.OPEN) {
104
130
  return;
105
131
  }
106
- const encoded = encodeDaemonToServiceMessage({ events, kind: SESSION_EVENT_KIND });
107
- if (socket.bufferedAmount + Buffer.byteLength(encoded, "utf8") > MAX_BUFFERED_BYTES) {
108
- socket.close(LOCAL_BACKPRESSURE_CLOSE_CODE, "Live event transport overloaded");
109
- return;
110
- }
111
- try {
112
- socket.send(encoded);
132
+ let attempted = 0;
133
+ for (const batch of this.#pending) {
134
+ const encoded = encodeDaemonToServiceMessage({ events: batch.events, kind: SESSION_EVENT_KIND });
135
+ if (socket.bufferedAmount + Buffer.byteLength(encoded, "utf8") > MAX_BUFFERED_BYTES) {
136
+ socket.close(LOCAL_BACKPRESSURE_CLOSE_CODE, "Live event transport overloaded");
137
+ break;
138
+ }
139
+ // Once handed to the socket, a batch cannot be safely resent without acknowledgements.
140
+ attempted += 1;
141
+ this.#pendingBytes -= batch.bytes;
142
+ try {
143
+ socket.send(encoded);
144
+ }
145
+ catch {
146
+ console.warn("Live Session output could not be sent", { code: "live_event_send_failed" });
147
+ break;
148
+ }
113
149
  }
114
- catch {
115
- // Socket liveness owns reconnection. Missed events are recovered from the local cache.
150
+ this.#pending.splice(0, attempted);
151
+ if (this.#pending.length === 0) {
152
+ this.#overflowReported = false;
116
153
  }
117
154
  };
118
155
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-event-publisher.js","sourceRoot":"","sources":["../../../src/runtime/relay/session-event-publisher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-event-publisher.js","sourceRoot":"","sources":["../../../src/runtime/relay/session-event-publisher.ts"],"names":[],"mappings":"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"}
package/dist/runtime.js CHANGED
@@ -22582,7 +22582,7 @@ import { fetch as fetch3 } from "undici";
22582
22582
  // package.json
22583
22583
  var package_default = {
22584
22584
  name: "@moxt-ai/mobius",
22585
- version: "0.0.295",
22585
+ version: "0.0.296",
22586
22586
  description: "Mobius CLI for pairing and running local AI agent services.",
22587
22587
  type: "module",
22588
22588
  bin: {
@@ -90231,37 +90231,44 @@ async function runRelayConnection({
90231
90231
  // src/runtime/relay/session-event-publisher.ts
90232
90232
  import { WebSocket as WebSocket8 } from "undici";
90233
90233
  var MAX_BUFFERED_BYTES = 32 * 1024 * 1024;
90234
+ var MAX_PENDING_BYTES2 = 32 * 1024 * 1024;
90234
90235
  var MAX_EVENT_BATCH_BYTES = 1024 * 1024;
90235
90236
  var MAX_FRAME_BYTES = 8 * 1024 * 1024;
90236
90237
  var LOCAL_BACKPRESSURE_CLOSE_CODE = 4009;
90237
90238
  var EMPTY_ENVELOPE_BYTES = Buffer.byteLength(JSON.stringify({ events: [], kind: SESSION_EVENT_KIND }));
90238
90239
  var SESSION_EVENT_BATCH_WINDOW_MILLISECONDS = 100;
90239
90240
  var RuntimeSessionEventPublisher = class {
90240
- #pendingEvents = [];
90241
- #pendingBytes = EMPTY_ENVELOPE_BYTES;
90241
+ #pending = [];
90242
+ #pendingBytes = 0;
90243
+ #overflowReported = false;
90242
90244
  #pendingTask = null;
90243
- #socket = null;
90245
+ #connection = { kind: "disconnected" };
90244
90246
  #timer;
90245
90247
  constructor(timer) {
90246
90248
  this.#timer = timer;
90247
90249
  }
90248
90250
  connect = (socket) => {
90249
- this.dropPendingEvents();
90250
- this.#socket = socket;
90251
+ if (this.#connection.kind === "closed") {
90252
+ return;
90253
+ }
90254
+ this.#connection = { kind: "connected", socket };
90255
+ this.flushEvents(socket);
90251
90256
  };
90252
90257
  disconnect = (socket) => {
90253
- if (this.#socket === socket) {
90254
- this.#socket = null;
90255
- this.dropPendingEvents();
90258
+ if (this.#connection.kind === "connected" && this.#connection.socket === socket) {
90259
+ this.#connection = { kind: "disconnected" };
90260
+ this.cancelFlush();
90256
90261
  }
90257
90262
  };
90258
90263
  close = () => {
90259
- this.#socket = null;
90260
- this.dropPendingEvents();
90264
+ this.#connection = { kind: "closed" };
90265
+ this.cancelFlush();
90266
+ this.#pending.length = 0;
90267
+ this.#pendingBytes = 0;
90261
90268
  };
90262
90269
  flush = () => {
90263
- if (this.#socket !== null) {
90264
- this.flushEvents(this.#socket);
90270
+ if (this.#connection.kind === "connected") {
90271
+ this.flushEvents(this.#connection.socket);
90265
90272
  }
90266
90273
  };
90267
90274
  publishOperationFailure = (sessionId, requestId, code, message) => {
@@ -90271,10 +90278,11 @@ var RuntimeSessionEventPublisher = class {
90271
90278
  publishInfo = (event) => this.#send(event);
90272
90279
  publishClosed = (event) => this.#send(event);
90273
90280
  #send = (message) => {
90274
- const socket = this.#socket;
90275
- if (socket === null || socket.readyState !== WebSocket8.OPEN) {
90281
+ const connection = this.#connection;
90282
+ if (connection.kind !== "connected" || connection.socket.readyState !== WebSocket8.OPEN) {
90276
90283
  return;
90277
90284
  }
90285
+ const socket = connection.socket;
90278
90286
  const encoded = encodeDaemonToServiceMessage(message);
90279
90287
  if (socket.bufferedAmount + Buffer.byteLength(encoded, "utf8") > MAX_BUFFERED_BYTES) {
90280
90288
  socket.close(LOCAL_BACKPRESSURE_CLOSE_CODE, "Live event transport overloaded");
@@ -90286,8 +90294,7 @@ var RuntimeSessionEventPublisher = class {
90286
90294
  }
90287
90295
  };
90288
90296
  publish = (event) => {
90289
- const socket = this.#socket;
90290
- if (socket === null || socket.readyState !== WebSocket8.OPEN) {
90297
+ if (this.#connection.kind === "closed") {
90291
90298
  return;
90292
90299
  }
90293
90300
  const bytes = Buffer.byteLength(JSON.stringify(event), "utf8") + 1;
@@ -90300,15 +90307,34 @@ var RuntimeSessionEventPublisher = class {
90300
90307
  });
90301
90308
  return;
90302
90309
  }
90303
- if (this.#pendingEvents.length > 0 && this.#pendingBytes + bytes > MAX_EVENT_BATCH_BYTES) {
90304
- this.flushEvents(socket);
90305
- if (socket.readyState !== WebSocket8.OPEN) {
90306
- return;
90310
+ let batch = this.#pending.at(-1);
90311
+ const newBatch = batch === void 0 || batch.events.length >= SESSION_EVENT_BATCH_MAX_SIZE || batch.bytes + bytes > MAX_EVENT_BATCH_BYTES;
90312
+ const additionalBytes = bytes + (newBatch ? EMPTY_ENVELOPE_BYTES : 0);
90313
+ if (this.#pendingBytes + additionalBytes > MAX_PENDING_BYTES2) {
90314
+ if (!this.#overflowReported) {
90315
+ this.#overflowReported = true;
90316
+ console.warn("Live Session output exceeded the pending buffer limit", {
90317
+ code: "live_event_buffer_full",
90318
+ sessionId: event.sessionId,
90319
+ eventId: event.eventId,
90320
+ maximumBytes: MAX_PENDING_BYTES2
90321
+ });
90307
90322
  }
90323
+ return;
90308
90324
  }
90309
- this.#pendingEvents.push(event);
90310
- this.#pendingBytes += bytes;
90311
- if (this.#pendingEvents.length >= SESSION_EVENT_BATCH_MAX_SIZE || this.#pendingBytes >= MAX_EVENT_BATCH_BYTES) {
90325
+ if (batch === void 0 || newBatch) {
90326
+ batch = { events: [], bytes: EMPTY_ENVELOPE_BYTES };
90327
+ this.#pending.push(batch);
90328
+ }
90329
+ batch.events.push(event);
90330
+ batch.bytes += bytes;
90331
+ this.#pendingBytes += additionalBytes;
90332
+ const connection = this.#connection;
90333
+ if (connection.kind !== "connected" || connection.socket.readyState !== WebSocket8.OPEN) {
90334
+ return;
90335
+ }
90336
+ const socket = connection.socket;
90337
+ if (this.#pending.length > 1 || batch.events.length >= SESSION_EVENT_BATCH_MAX_SIZE || batch.bytes >= MAX_EVENT_BATCH_BYTES) {
90312
90338
  this.flushEvents(socket);
90313
90339
  return;
90314
90340
  }
@@ -90317,29 +90343,35 @@ var RuntimeSessionEventPublisher = class {
90317
90343
  SESSION_EVENT_BATCH_WINDOW_MILLISECONDS
90318
90344
  );
90319
90345
  };
90320
- dropPendingEvents = () => {
90346
+ cancelFlush = () => {
90321
90347
  this.#pendingTask?.cancel();
90322
90348
  this.#pendingTask = null;
90323
- this.#pendingEvents = [];
90324
- this.#pendingBytes = EMPTY_ENVELOPE_BYTES;
90325
90349
  };
90326
90350
  flushEvents = (socket) => {
90327
- this.#pendingTask?.cancel();
90328
- this.#pendingTask = null;
90329
- const events = this.#pendingEvents;
90330
- this.#pendingEvents = [];
90331
- this.#pendingBytes = EMPTY_ENVELOPE_BYTES;
90332
- if (events.length === 0 || this.#socket !== socket || socket.readyState !== WebSocket8.OPEN) {
90351
+ this.cancelFlush();
90352
+ const connection = this.#connection;
90353
+ if (connection.kind !== "connected" || connection.socket !== socket || socket.readyState !== WebSocket8.OPEN) {
90333
90354
  return;
90334
90355
  }
90335
- const encoded = encodeDaemonToServiceMessage({ events, kind: SESSION_EVENT_KIND });
90336
- if (socket.bufferedAmount + Buffer.byteLength(encoded, "utf8") > MAX_BUFFERED_BYTES) {
90337
- socket.close(LOCAL_BACKPRESSURE_CLOSE_CODE, "Live event transport overloaded");
90338
- return;
90356
+ let attempted = 0;
90357
+ for (const batch of this.#pending) {
90358
+ const encoded = encodeDaemonToServiceMessage({ events: batch.events, kind: SESSION_EVENT_KIND });
90359
+ if (socket.bufferedAmount + Buffer.byteLength(encoded, "utf8") > MAX_BUFFERED_BYTES) {
90360
+ socket.close(LOCAL_BACKPRESSURE_CLOSE_CODE, "Live event transport overloaded");
90361
+ break;
90362
+ }
90363
+ attempted += 1;
90364
+ this.#pendingBytes -= batch.bytes;
90365
+ try {
90366
+ socket.send(encoded);
90367
+ } catch {
90368
+ console.warn("Live Session output could not be sent", { code: "live_event_send_failed" });
90369
+ break;
90370
+ }
90339
90371
  }
90340
- try {
90341
- socket.send(encoded);
90342
- } catch {
90372
+ this.#pending.splice(0, attempted);
90373
+ if (this.#pending.length === 0) {
90374
+ this.#overflowReported = false;
90343
90375
  }
90344
90376
  };
90345
90377
  };