@openclaw/discord 2026.6.8 → 2026.6.9

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 (114) hide show
  1. package/dist/action-runtime-api.js +1 -1
  2. package/dist/{api-BmXlcjYW.js → api-4GFMoWu0.js} +4 -1
  3. package/dist/api.js +15 -15
  4. package/dist/{approval-handler.runtime-B3gyUd-L.js → approval-handler.runtime-20qhf0uH.js} +3 -3
  5. package/dist/{audit-BlfewK04.js → audit-CCffO6o7.js} +3 -3
  6. package/dist/{channel-actions-BnPHwCZ_.js → channel-actions-DDT9kJXw.js} +12 -6
  7. package/dist/{channel-actions.runtime-DX5iW6ut.js → channel-actions.runtime-CfMUtfAv.js} +5 -5
  8. package/dist/{channel-b0hY1EJw.js → channel-fEBKaO2Q.js} +28 -20
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/{channel.setup-By5cfELZ.js → channel.setup-DRJiHe7I.js} +2 -3
  11. package/dist/{components-D-CYw0-b.js → components-CXEorOhu.js} +1 -1
  12. package/dist/{conversation-identity-CKzQAqFF.js → conversation-identity-BvlP-djn.js} +2 -2
  13. package/dist/{directory-config-Bu7FYOsl.js → directory-config-Bg8bulWB.js} +1 -1
  14. package/dist/directory-contract-api.js +1 -1
  15. package/dist/{directory-live-LjENjK6L.js → directory-live-BAJw6kS2.js} +2 -2
  16. package/dist/{handle-action.guild-admin-BS2qnhXF.js → handle-action.guild-admin-RxnJFLgU.js} +14 -9
  17. package/dist/index.js +1 -1
  18. package/dist/{manager.runtime-BU1vkOeO.js → manager.runtime-Bh5X0o8P.js} +12 -4
  19. package/dist/{message-handler-CQVkXHMN.js → message-handler-Dg3DRD0e.js} +6 -6
  20. package/dist/{message-handler.preflight-DAnLOeDA.js → message-handler.preflight-CcexCYuy.js} +10 -10
  21. package/dist/{message-handler.process-Dp5NQT05.js → message-handler.process-CN8bVDxj.js} +37 -45
  22. package/dist/{message-utils-Bx993JLN.js → message-utils-ConlUKCU.js} +1 -1
  23. package/dist/{outbound-adapter-CmN7ao1t.js → outbound-adapter-XuMiR77A.js} +6 -6
  24. package/dist/{pluralkit-SYmlmerw.js → pluralkit-o6s-EP9g.js} +4 -2
  25. package/dist/{probe-2MdGjUAf.js → probe-CxnDikWm.js} +5 -2
  26. package/dist/{probe.runtime-J927koKG.js → probe.runtime-DLFkSDVB.js} +1 -1
  27. package/dist/{provider-DrScDA1p.js → provider-4VG5CvC-.js} +98 -69
  28. package/dist/{provider-session.runtime-DXTzSYOJ.js → provider-session.runtime-ChdHgGCo.js} +3 -3
  29. package/dist/provider.runtime-B4QuZl_o.js +2 -0
  30. package/dist/{resolve-allowlist-common-B3p7cT8F.js → resolve-allowlist-common-BDpBb8dF.js} +1 -1
  31. package/dist/{resolve-channels-Rautpk8n.js → resolve-channels-DpM2v0Am.js} +3 -3
  32. package/dist/{resolve-users-Bw7vvtsi.js → resolve-users-Dl6np4pH.js} +3 -3
  33. package/dist/{runtime-C80YEJ7Z.js → runtime-CkdyyjTb.js} +24 -10
  34. package/dist/runtime-api.actions.js +2 -2
  35. package/dist/runtime-api.js +19 -19
  36. package/dist/runtime-api.lookup.js +5 -5
  37. package/dist/runtime-api.monitor-9pShEBWs.js +6 -0
  38. package/dist/runtime-api.monitor.js +4 -4
  39. package/dist/runtime-api.send.js +5 -5
  40. package/dist/runtime-api.threads.js +3 -3
  41. package/dist/{send-zGsXF-up.js → send-CIhsDMqs.js} +8 -5
  42. package/dist/{send.components-ktzrUTkt.js → send.components-DxoGyTkn.js} +4 -4
  43. package/dist/{send.outbound-C8oC51um.js → send.outbound-DaUyYuZa.js} +53 -10
  44. package/dist/{send.receipt-DsQWEQ2O.js → send.receipt-De_2iEWU.js} +2 -24
  45. package/dist/{send.shared-Dvo2ZCVG.js → send.shared-gtgBGDuV.js} +13 -5
  46. package/dist/session-binding-contract-api.js +1 -1
  47. package/dist/setup-plugin-api.js +1 -1
  48. package/dist/{subagent-hooks-kjrWDeDg.js → subagent-hooks-DPCipg2M.js} +2 -2
  49. package/dist/subagent-hooks-api.js +1 -1
  50. package/dist/{system-events-CvU3Aduf.js → system-events-4vmuyaBF.js} +1 -1
  51. package/dist/{target-resolver-DMPTzuo7.js → target-resolver-CVrOhQyC.js} +2 -2
  52. package/dist/targets-CRGgRKs7.js +3 -0
  53. package/dist/{thread-bindings-DO32M2kW.js → thread-bindings-D2SBj6F8.js} +4 -4
  54. package/dist/{thread-bindings.discord-api-304M1PMr.js → thread-bindings.discord-api-C7STOVXm.js} +4 -4
  55. package/dist/{thread-bindings.manager-C9YT7wF2.js → thread-bindings.manager-CN4MIGoJ.js} +3 -3
  56. package/dist/{transcripts-source-Chy2OrO_.js → transcripts-source-39NBdNeE.js} +1 -1
  57. package/dist/transcripts-source-api.js +1 -1
  58. package/dist/{typing-BaivbXIG.js → typing-CxIYS3vQ.js} +1 -1
  59. package/node_modules/undici/README.md +59 -18
  60. package/node_modules/undici/docs/docs/GettingStarted.md +278 -0
  61. package/node_modules/undici/docs/docs/api/Agent.md +3 -0
  62. package/node_modules/undici/docs/docs/api/BalancedPool.md +1 -1
  63. package/node_modules/undici/docs/docs/api/Client.md +44 -5
  64. package/node_modules/undici/docs/docs/api/Connector.md +1 -0
  65. package/node_modules/undici/docs/docs/api/Cookies.md +28 -1
  66. package/node_modules/undici/docs/docs/api/Dispatcher.md +22 -5
  67. package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +6 -9
  68. package/node_modules/undici/docs/docs/api/Errors.md +12 -0
  69. package/node_modules/undici/docs/docs/api/EventSource.md +50 -3
  70. package/node_modules/undici/docs/docs/api/Fetch.md +5 -3
  71. package/node_modules/undici/docs/docs/api/H2CClient.md +3 -3
  72. package/node_modules/undici/docs/docs/api/MockAgent.md +1 -1
  73. package/node_modules/undici/docs/docs/api/MockCallHistory.md +1 -1
  74. package/node_modules/undici/docs/docs/api/Pool.md +4 -1
  75. package/node_modules/undici/docs/docs/api/RedirectHandler.md +4 -1
  76. package/node_modules/undici/docs/docs/api/RetryAgent.md +3 -3
  77. package/node_modules/undici/docs/docs/api/RetryHandler.md +6 -6
  78. package/node_modules/undici/docs/docs/api/RoundRobinPool.md +1 -1
  79. package/node_modules/undici/docs/docs/api/SnapshotAgent.md +3 -3
  80. package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +1 -0
  81. package/node_modules/undici/docs/docs/api/api-lifecycle.md +4 -4
  82. package/node_modules/undici/lib/core/connect.js +29 -4
  83. package/node_modules/undici/lib/core/util.js +8 -6
  84. package/node_modules/undici/lib/dispatcher/client-h1.js +69 -2
  85. package/node_modules/undici/lib/dispatcher/client-h2.js +160 -37
  86. package/node_modules/undici/lib/dispatcher/client.js +36 -7
  87. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +1 -0
  88. package/node_modules/undici/lib/dispatcher/proxy-agent.js +2 -1
  89. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +4 -2
  90. package/node_modules/undici/lib/handler/redirect-handler.js +36 -11
  91. package/node_modules/undici/lib/interceptor/dns.js +4 -0
  92. package/node_modules/undici/lib/interceptor/redirect.js +3 -3
  93. package/node_modules/undici/lib/mock/mock-call-history.js +1 -1
  94. package/node_modules/undici/lib/mock/snapshot-agent.js +9 -1
  95. package/node_modules/undici/lib/util/cache.js +8 -2
  96. package/node_modules/undici/lib/web/cookies/parse.js +17 -25
  97. package/node_modules/undici/lib/web/eventsource/eventsource.js +7 -18
  98. package/node_modules/undici/lib/web/eventsource/util.js +32 -1
  99. package/node_modules/undici/lib/web/fetch/body.js +43 -0
  100. package/node_modules/undici/lib/web/fetch/index.js +17 -3
  101. package/node_modules/undici/lib/web/fetch/request.js +33 -3
  102. package/node_modules/undici/lib/web/websocket/receiver.js +20 -3
  103. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +8 -1
  104. package/node_modules/undici/lib/web/websocket/websocket.js +3 -1
  105. package/node_modules/undici/package.json +1 -1
  106. package/node_modules/undici/types/client.d.ts +5 -0
  107. package/node_modules/undici/types/connector.d.ts +1 -0
  108. package/node_modules/undici/types/fetch.d.ts +5 -1
  109. package/node_modules/undici/types/interceptors.d.ts +1 -1
  110. package/npm-shrinkwrap.json +7 -7
  111. package/package.json +5 -5
  112. package/dist/provider.runtime-nb-6cRoy.js +0 -2
  113. package/dist/runtime-api.monitor-D8KNDAd5.js +0 -6
  114. package/dist/targets-CKaNidbk.js +0 -3
@@ -57,6 +57,9 @@ const constants = require('../llhttp/constants.js')
57
57
  const EMPTY_BUF = Buffer.alloc(0)
58
58
  const FastBuffer = Buffer[Symbol.species]
59
59
  const removeAllListeners = util.removeAllListeners
60
+ const kIdleSocketValidation = Symbol('kIdleSocketValidation')
61
+ const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout')
62
+ const kSocketUsed = Symbol('kSocketUsed')
60
63
 
61
64
  let extractBody
62
65
 
@@ -371,7 +374,6 @@ class Parser {
371
374
  finish () {
372
375
  assert(currentParser === null)
373
376
  assert(this.ptr != null)
374
- assert(!this.paused)
375
377
 
376
378
  const { llhttp } = this
377
379
 
@@ -450,6 +452,11 @@ class Parser {
450
452
  return -1
451
453
  }
452
454
 
455
+ if (client[kRunning] === 0) {
456
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
457
+ return -1
458
+ }
459
+
453
460
  const request = client[kQueue][client[kRunningIdx]]
454
461
  if (!request) {
455
462
  return -1
@@ -585,6 +592,11 @@ class Parser {
585
592
  return -1
586
593
  }
587
594
 
595
+ if (client[kRunning] === 0) {
596
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
597
+ return -1
598
+ }
599
+
588
600
  const request = client[kQueue][client[kRunningIdx]]
589
601
 
590
602
  if (!request) {
@@ -763,6 +775,7 @@ class Parser {
763
775
  request.onResponseEnd(headers)
764
776
 
765
777
  client[kQueue][client[kRunningIdx]++] = null
778
+ socket[kSocketUsed] = client[kPending] === 0
766
779
 
767
780
  if (socket[kWriting]) {
768
781
  assert(client[kRunning] === 0)
@@ -839,6 +852,9 @@ function connectH1 (client, socket) {
839
852
  socket[kWriting] = false
840
853
  socket[kReset] = false
841
854
  socket[kBlocking] = false
855
+ socket[kIdleSocketValidation] = 0
856
+ socket[kIdleSocketValidationTimeout] = null
857
+ socket[kSocketUsed] = false
842
858
  socket[kParser] = new Parser(client, socket, llhttpInstance)
843
859
 
844
860
  util.addListener(socket, 'error', onHttpSocketError)
@@ -881,7 +897,7 @@ function connectH1 (client, socket) {
881
897
  * @returns {boolean}
882
898
  */
883
899
  busy (request) {
884
- if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
900
+ if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) {
885
901
  return true
886
902
  }
887
903
 
@@ -961,6 +977,8 @@ function onHttpSocketEnd () {
961
977
  function onHttpSocketClose () {
962
978
  const parser = this[kParser]
963
979
 
980
+ clearIdleSocketValidation(this)
981
+
964
982
  if (parser) {
965
983
  if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
966
984
  this[kError] = parser.finish() || this[kError]
@@ -1007,6 +1025,28 @@ function onSocketClose () {
1007
1025
  this[kClosed] = true
1008
1026
  }
1009
1027
 
1028
+ function clearIdleSocketValidation (socket) {
1029
+ if (socket[kIdleSocketValidationTimeout]) {
1030
+ clearTimeout(socket[kIdleSocketValidationTimeout])
1031
+ socket[kIdleSocketValidationTimeout] = null
1032
+ }
1033
+
1034
+ socket[kIdleSocketValidation] = 0
1035
+ }
1036
+
1037
+ function scheduleIdleSocketValidation (client, socket) {
1038
+ socket[kIdleSocketValidation] = 1
1039
+ socket[kIdleSocketValidationTimeout] = setTimeout(() => {
1040
+ socket[kIdleSocketValidationTimeout] = null
1041
+ socket[kIdleSocketValidation] = 2
1042
+
1043
+ if (client[kSocket] === socket && !socket.destroyed) {
1044
+ client[kResume]()
1045
+ }
1046
+ }, 0)
1047
+ socket[kIdleSocketValidationTimeout].unref?.()
1048
+ }
1049
+
1010
1050
  /**
1011
1051
  * @param {import('./client.js')} client
1012
1052
  */
@@ -1024,6 +1064,32 @@ function resumeH1 (client) {
1024
1064
  socket[kNoRef] = false
1025
1065
  }
1026
1066
 
1067
+ if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) {
1068
+ if (socket[kIdleSocketValidation] === 0) {
1069
+ scheduleIdleSocketValidation(client, socket)
1070
+ socket[kParser].readMore()
1071
+ if (socket.destroyed) {
1072
+ return
1073
+ }
1074
+ return
1075
+ }
1076
+
1077
+ if (socket[kIdleSocketValidation] === 1) {
1078
+ socket[kParser].readMore()
1079
+ if (socket.destroyed) {
1080
+ return
1081
+ }
1082
+ return
1083
+ }
1084
+ }
1085
+
1086
+ if (client[kRunning] === 0) {
1087
+ socket[kParser].readMore()
1088
+ if (socket.destroyed) {
1089
+ return
1090
+ }
1091
+ }
1092
+
1027
1093
  if (client[kSize] === 0) {
1028
1094
  if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
1029
1095
  socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
@@ -1122,6 +1188,7 @@ function writeH1 (client, request) {
1122
1188
  }
1123
1189
 
1124
1190
  const socket = client[kSocket]
1191
+ clearIdleSocketValidation(socket)
1125
1192
 
1126
1193
  /**
1127
1194
  * @param {Error} [err]
@@ -8,7 +8,9 @@ const {
8
8
  RequestAbortedError,
9
9
  SocketError,
10
10
  InformationalError,
11
- InvalidArgumentError
11
+ InvalidArgumentError,
12
+ HeadersTimeoutError,
13
+ BodyTimeoutError
12
14
  } = require('../core/errors.js')
13
15
  const {
14
16
  kUrl,
@@ -33,6 +35,8 @@ const {
33
35
  kSize,
34
36
  kHTTPContext,
35
37
  kClosed,
38
+ kKeepAliveDefaultTimeout,
39
+ kHeadersTimeout,
36
40
  kBodyTimeout,
37
41
  kEnableConnectProtocol,
38
42
  kRemoteSettings,
@@ -81,6 +85,29 @@ function getGoAwayError (session, errorCode) {
81
85
  : new SocketError(`HTTP/2: "GOAWAY" frame received with code ${errorCode}`, util.getSocketInfo(session[kSocket])))
82
86
  }
83
87
 
88
+ function resetHttp2Session (session, err) {
89
+ const client = session[kClient]
90
+ const socket = session[kSocket]
91
+
92
+ if (client[kHTTP2Session] === session) {
93
+ client[kSocket] = null
94
+ client[kHTTPContext] = null
95
+ client[kHTTP2Session] = null
96
+ }
97
+
98
+ if (socket != null && socket[kError] == null) {
99
+ socket[kError] = err
100
+ }
101
+
102
+ if (!session.closed && !session.destroyed) {
103
+ try {
104
+ session.destroy(err)
105
+ } catch {}
106
+ }
107
+
108
+ util.destroy(socket, err)
109
+ }
110
+
84
111
  function getGoAwayPendingIdx (client, lastStreamID) {
85
112
  const maxAcceptedStreamID = Number.isInteger(lastStreamID) ? lastStreamID : Number.MAX_SAFE_INTEGER
86
113
 
@@ -122,6 +149,25 @@ function clearRequestStream (request) {
122
149
  cleanup?.(stream)
123
150
  }
124
151
 
152
+ function requeueUnsentRequest (client, request) {
153
+ client[kQueue].splice(client[kPendingIdx] + 1, 0, request)
154
+ }
155
+
156
+ function completeRequest (client, request, resetPendingIdx = false) {
157
+ const index = client[kQueue].indexOf(request, client[kRunningIdx])
158
+
159
+ if (index === -1 || index >= client[kPendingIdx]) {
160
+ return
161
+ }
162
+
163
+ client[kQueue].splice(index, 1)
164
+ client[kPendingIdx]--
165
+
166
+ if (resetPendingIdx && client[kPendingIdx] < client[kRunningIdx]) {
167
+ client[kPendingIdx] = client[kRunningIdx]
168
+ }
169
+ }
170
+
125
171
  function canRetryRequestAfterGoAway (request) {
126
172
  const { body } = request
127
173
 
@@ -161,6 +207,7 @@ function connectH2 (client, socket) {
161
207
  session[kClient] = client
162
208
  session[kSocket] = socket
163
209
  session[kHTTP2SessionState] = {
210
+ idleTimeout: null,
164
211
  ping: {
165
212
  interval: client[kPingInterval] === 0 ? null : setInterval(onHttp2SendPing, client[kPingInterval], session).unref()
166
213
  }
@@ -249,10 +296,10 @@ function connectH2 (client, socket) {
249
296
  if (client[kRunning] > 0) {
250
297
  // We are already processing requests
251
298
 
252
- // Non-idempotent request cannot be retried.
253
- // Ensure that no other requests are inflight and
254
- // could cause failure.
255
- if (request.idempotent === false) return true
299
+ // Unlike HTTP/1.1 pipelining, HTTP/2 multiplexes requests on
300
+ // independent streams, so non-idempotent requests can be dispatched
301
+ // concurrently. Retry eligibility is handled by stream/session error
302
+ // handling instead of by serializing all non-idempotent requests.
256
303
  // Don't dispatch an upgrade until all preceding requests have completed.
257
304
  // Possibly, we do not have remote settings confirmed yet.
258
305
  if ((request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false) return true
@@ -278,16 +325,66 @@ function connectH2 (client, socket) {
278
325
 
279
326
  function resumeH2 (client) {
280
327
  const socket = client[kSocket]
328
+ const session = client[kHTTP2Session]
281
329
 
282
330
  if (socket?.destroyed === false) {
283
331
  if (client[kSize] === 0 || client[kMaxConcurrentStreams] === 0) {
284
332
  socket.unref()
285
- client[kHTTP2Session].unref()
333
+ session.unref()
286
334
  } else {
287
335
  socket.ref()
288
- client[kHTTP2Session].ref()
336
+ session.ref()
289
337
  }
338
+
339
+ if (client[kSize] === 0 && session[kOpenStreams] === 0) {
340
+ setHttp2IdleTimeout(session)
341
+ } else {
342
+ clearHttp2IdleTimeout(session)
343
+ }
344
+ }
345
+ }
346
+
347
+ function clearHttp2IdleTimeout (session) {
348
+ const state = session[kHTTP2SessionState]
349
+
350
+ if (state?.idleTimeout != null) {
351
+ clearTimeout(state.idleTimeout)
352
+ state.idleTimeout = null
353
+ }
354
+ }
355
+
356
+ function setHttp2IdleTimeout (session) {
357
+ const client = session[kClient]
358
+
359
+ if (client[kHTTP2Session] !== session || session.closed || session.destroyed) {
360
+ return
361
+ }
362
+
363
+ if (session[kOpenStreams] !== 0 || client[kSize] !== 0) {
364
+ clearHttp2IdleTimeout(session)
365
+ return
366
+ }
367
+
368
+ const state = session[kHTTP2SessionState]
369
+ if (state.idleTimeout == null) {
370
+ state.idleTimeout = setTimeout(onHttp2SessionIdleTimeout, client[kKeepAliveDefaultTimeout], session).unref()
371
+ }
372
+ }
373
+
374
+ function onHttp2SessionIdleTimeout (session) {
375
+ const client = session[kClient]
376
+ const socket = session[kSocket]
377
+ const state = session[kHTTP2SessionState]
378
+
379
+ state.idleTimeout = null
380
+
381
+ if (client[kHTTP2Session] !== session || session[kOpenStreams] !== 0 || client[kSize] !== 0 || session.closed || session.destroyed) {
382
+ return
290
383
  }
384
+
385
+ const err = new InformationalError('socket idle timeout')
386
+ socket[kError] = err
387
+ util.destroy(socket, err)
291
388
  }
292
389
 
293
390
  function applyConnectionWindowSize (connectionWindowSize) {
@@ -415,6 +512,8 @@ function onHttp2SessionGoAway (errorCode, lastStreamID) {
415
512
  client[kHTTP2Session] = null
416
513
  }
417
514
 
515
+ clearHttp2IdleTimeout(this)
516
+
418
517
  if (!this.closed && !this.destroyed) {
419
518
  this.close()
420
519
  }
@@ -437,6 +536,8 @@ function onHttp2SessionClose () {
437
536
  client[kHTTP2Session] = null
438
537
  }
439
538
 
539
+ clearHttp2IdleTimeout(this)
540
+
440
541
  if (state.ping.interval != null) {
441
542
  clearInterval(state.ping.interval)
442
543
  state.ping.interval = null
@@ -449,7 +550,9 @@ function onHttp2SessionClose () {
449
550
  const requests = client[kQueue].splice(client[kRunningIdx])
450
551
  for (let i = 0; i < requests.length; i++) {
451
552
  const request = requests[i]
452
- util.errorRequest(client, request, err)
553
+ if (request != null) {
554
+ util.errorRequest(client, request, err)
555
+ }
453
556
  }
454
557
  }
455
558
  }
@@ -512,6 +615,7 @@ function closeStreamSession (stream) {
512
615
  session[kOpenStreams] -= 1
513
616
  if (session[kOpenStreams] === 0) {
514
617
  session.unref()
618
+ setHttp2IdleTimeout(session)
515
619
  }
516
620
  }
517
621
 
@@ -526,6 +630,19 @@ function onUpgradeStreamClose () {
526
630
  }
527
631
 
528
632
  function onRequestStreamClose () {
633
+ const state = this[kRequestStreamState]
634
+
635
+ if (state) {
636
+ // Release the stream first so request references are cleared,
637
+ // then complete the response with trailers if available.
638
+ releaseRequestStream(this)
639
+
640
+ if (state.pendingEnd && !state.request.aborted && !state.request.completed) {
641
+ state.request.onResponseEnd(state.trailers || {})
642
+ state.finalizeRequest()
643
+ }
644
+ }
645
+
529
646
  this.off('data', onData)
530
647
  this.off('error', noop)
531
648
  closeStreamSession(this)
@@ -629,7 +746,7 @@ function onUpgradeStreamEnd () {
629
746
 
630
747
  function onUpgradeStreamTimeout () {
631
748
  const state = this[kRequestStreamState]
632
- failUpgradeStream(state, new InformationalError(`HTTP/2: "stream timeout after ${state.requestTimeout}"`))
749
+ failUpgradeStream(state, new InformationalError(`HTTP/2: "stream timeout after ${state.headersTimeout}"`))
633
750
  }
634
751
 
635
752
  function onUpgradeResponse (headers, _flags) {
@@ -654,7 +771,7 @@ function onUpgradeResponse (headers, _flags) {
654
771
  }
655
772
 
656
773
  function setupUpgradeStream (stream, state) {
657
- const { request, requestTimeout, session } = state
774
+ const { request, headersTimeout, session } = state
658
775
 
659
776
  stream[kHTTP2Stream] = true
660
777
  stream[kHTTP2Session] = session
@@ -668,12 +785,14 @@ function setupUpgradeStream (stream, state) {
668
785
  stream.on('timeout', onUpgradeStreamTimeout)
669
786
  stream.once('close', onUpgradeStreamClose)
670
787
 
788
+ clearHttp2IdleTimeout(session)
671
789
  ++session[kOpenStreams]
672
- stream.setTimeout(requestTimeout)
790
+ stream.setTimeout(headersTimeout)
673
791
  }
674
792
 
675
793
  function writeH2 (client, request) {
676
- const requestTimeout = request.bodyTimeout ?? client[kBodyTimeout]
794
+ const headersTimeout = request.headersTimeout ?? client[kHeadersTimeout]
795
+ const bodyTimeout = request.bodyTimeout ?? client[kBodyTimeout]
677
796
  const session = client[kHTTP2Session]
678
797
  const { method, path, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request
679
798
  let { body } = request
@@ -698,11 +817,7 @@ function writeH2 (client, request) {
698
817
  }
699
818
 
700
819
  requestFinalized = true
701
- client[kQueue][client[kRunningIdx]++] = null
702
-
703
- if (resetPendingIdx) {
704
- client[kPendingIdx] = client[kRunningIdx]
705
- }
820
+ completeRequest(client, request, resetPendingIdx)
706
821
 
707
822
  client[kResume]()
708
823
  }
@@ -736,8 +851,14 @@ function writeH2 (client, request) {
736
851
  try {
737
852
  return session.request(headers, options)
738
853
  } catch (err) {
739
- if (err?.code !== 'ERR_HTTP2_INVALID_CONNECTION_HEADERS') {
740
- throw err
854
+ if (err?.code === 'ERR_HTTP2_INVALID_SESSION') {
855
+ const wrappedErr = new SocketError(err.message, util.getSocketInfo(session[kSocket]))
856
+ wrappedErr.cause = err
857
+ session[kError] = wrappedErr
858
+ resetHttp2Session(session, wrappedErr)
859
+ requeueUnsentRequest(client, request)
860
+
861
+ return null
741
862
  }
742
863
 
743
864
  const wrappedErr = new InformationalError(err.message, { cause: err })
@@ -771,7 +892,8 @@ function writeH2 (client, request) {
771
892
  abort,
772
893
  finalizeRequest,
773
894
  request,
774
- requestTimeout,
895
+ headersTimeout,
896
+ bodyTimeout,
775
897
  responseReceived: false,
776
898
  session,
777
899
  stream: null
@@ -912,7 +1034,8 @@ function writeH2 (client, request) {
912
1034
  expectsPayload,
913
1035
  finalizeRequest,
914
1036
  request,
915
- requestTimeout,
1037
+ headersTimeout,
1038
+ bodyTimeout,
916
1039
  responseReceived: false,
917
1040
  session,
918
1041
  stream: null
@@ -929,11 +1052,11 @@ function writeH2 (client, request) {
929
1052
  stream[kHTTP2Stream] = true
930
1053
  stream[kRequestStreamState] = state
931
1054
  state.stream = stream
932
- bindRequestToStream(request, stream, null)
933
1055
 
934
1056
  // Increment counter as we have new streams open
1057
+ clearHttp2IdleTimeout(session)
935
1058
  ++session[kOpenStreams]
936
- stream.setTimeout(requestTimeout)
1059
+ stream.setTimeout(headersTimeout)
937
1060
 
938
1061
  stream[kHTTP2Session] = session
939
1062
  stream.once('close', onRequestStreamClose)
@@ -1017,6 +1140,7 @@ function onResponse (headers) {
1017
1140
  delete headers[HTTP2_HEADER_STATUS]
1018
1141
  request.onResponseStarted()
1019
1142
  state.responseReceived = true
1143
+ stream.setTimeout(state.bodyTimeout)
1020
1144
 
1021
1145
  // Due to the stream nature, it is possible we face a race condition
1022
1146
  // where the stream has been assigned, but the request has been aborted
@@ -1042,14 +1166,14 @@ function onEnd () {
1042
1166
 
1043
1167
  stream.off('end', onEnd)
1044
1168
 
1045
- releaseRequestStream(stream)
1046
- // If we received a response, this is a normal completion
1169
+ // If we received a response, this is a normal completion.
1170
+ // Defer actual completion to onRequestStreamClose so that
1171
+ // onTrailers (which may fire after 'end' on Windows) can
1172
+ // store trailers first.
1047
1173
  if (state.responseReceived) {
1048
1174
  if (!request.aborted && !request.completed) {
1049
- request.onResponseEnd({})
1175
+ state.pendingEnd = true
1050
1176
  }
1051
-
1052
- state.finalizeRequest()
1053
1177
  } else {
1054
1178
  // Stream ended without receiving a response - this is an error
1055
1179
  // (e.g., server destroyed the stream before sending headers)
@@ -1062,8 +1186,6 @@ function onError (err) {
1062
1186
  const state = stream[kRequestStreamState]
1063
1187
 
1064
1188
  stream.off('error', onError)
1065
-
1066
- releaseRequestStream(stream)
1067
1189
  state.abort(err)
1068
1190
  }
1069
1191
 
@@ -1072,8 +1194,6 @@ function onFrameError (type, code) {
1072
1194
  const state = stream[kRequestStreamState]
1073
1195
 
1074
1196
  stream.off('frameError', onFrameError)
1075
-
1076
- releaseRequestStream(stream)
1077
1197
  state.abort(new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`))
1078
1198
  }
1079
1199
 
@@ -1085,9 +1205,12 @@ function onTimeout () {
1085
1205
  const stream = this
1086
1206
  const state = stream[kRequestStreamState]
1087
1207
 
1088
- releaseRequestStream(stream)
1208
+ // Remove self so timeout doesn't fire again after we handle it
1209
+ stream.off('timeout', onTimeout)
1089
1210
 
1090
- const err = new InformationalError(`HTTP/2: "stream timeout after ${state.requestTimeout}"`)
1211
+ const err = state.responseReceived
1212
+ ? new BodyTimeoutError(`HTTP/2: "stream timeout after ${state.bodyTimeout}"`)
1213
+ : new HeadersTimeoutError(`HTTP/2: "headers timeout after ${state.headersTimeout}"`)
1091
1214
  state.abort(err)
1092
1215
  }
1093
1216
 
@@ -1097,14 +1220,14 @@ function onTrailers (trailers) {
1097
1220
  const { request } = state
1098
1221
 
1099
1222
  stream.off('trailers', onTrailers)
1223
+ stream.off('data', onData)
1100
1224
 
1101
1225
  if (request.aborted || request.completed) {
1102
1226
  return
1103
1227
  }
1104
1228
 
1105
- releaseRequestStream(stream)
1106
- request.onResponseEnd(trailers)
1107
- state.finalizeRequest()
1229
+ // Store trailers for onRequestStreamClose to use when completing
1230
+ state.trailers = trailers
1108
1231
  }
1109
1232
 
1110
1233
  function writeBodyH2 () {
@@ -76,6 +76,18 @@ function getPipelining (client) {
76
76
  return client[kPipelining] ?? client[kHTTPContext]?.defaultPipelining ?? 1
77
77
  }
78
78
 
79
+ // Protocol-aware dispatch ceiling. h1 RFC7230 pipelining is unrelated to h2
80
+ // stream multiplexing — over h2 the ceiling is the (server-confirmed)
81
+ // maxConcurrentStreams. Before a context is attached we use the h1
82
+ // pipelining factor; once h2 attaches the queued requests can drain in
83
+ // one batch up to maxConcurrentStreams.
84
+ function getMaxConcurrent (client) {
85
+ if (client[kHTTPContext]?.version === 'h2') {
86
+ return client[kMaxConcurrentStreams]
87
+ }
88
+ return getPipelining(client)
89
+ }
90
+
79
91
  /**
80
92
  * @type {import('../../types/client.js').default}
81
93
  */
@@ -326,10 +338,17 @@ class Client extends DispatcherBase {
326
338
  }
327
339
 
328
340
  get [kBusy] () {
341
+ // The `kPending > 0` check below is the gate Pool uses to decide whether
342
+ // to spin up an additional Client. For h1 that fan-out is correct —
343
+ // each socket only handles one pipelined request at a time. Once an h2
344
+ // context is attached we want concurrent dispatches to multiplex onto
345
+ // the shared session, so suppress that signal in the h2 case.
346
+ const allowsMux = this[kHTTPContext]?.version === 'h2'
347
+
329
348
  return Boolean(
330
349
  this[kHTTPContext]?.busy(null) ||
331
- (this[kSize] >= (getPipelining(this) || 1)) ||
332
- this[kPending] > 0
350
+ (this[kSize] >= (getMaxConcurrent(this) || 1)) ||
351
+ (this[kPending] > 0 && !allowsMux)
333
352
  )
334
353
  }
335
354
 
@@ -376,7 +395,9 @@ class Client extends DispatcherBase {
376
395
  const requests = this[kQueue].splice(this[kPendingIdx])
377
396
  for (let i = 0; i < requests.length; i++) {
378
397
  const request = requests[i]
379
- util.errorRequest(this, request, err)
398
+ if (request != null) {
399
+ util.errorRequest(this, request, err)
400
+ }
380
401
  }
381
402
 
382
403
  const callback = () => {
@@ -415,7 +436,9 @@ function onError (client, err) {
415
436
 
416
437
  for (let i = 0; i < requests.length; i++) {
417
438
  const request = requests[i]
418
- util.errorRequest(client, request, err)
439
+ if (request != null) {
440
+ util.errorRequest(client, request, err)
441
+ }
419
442
  }
420
443
  assert(client[kSize] === 0)
421
444
  }
@@ -549,9 +572,15 @@ function handleConnectError (client, err, { host, hostname, protocol, port }) {
549
572
  }
550
573
 
551
574
  if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
552
- assert(client[kRunning] === 0)
575
+ const running = client[kQueue].splice(client[kRunningIdx], client[kRunning])
576
+ client[kPendingIdx] = client[kRunningIdx]
577
+
578
+ for (let i = 0; i < running.length; i++) {
579
+ util.errorRequest(client, running[i], err)
580
+ }
581
+
553
582
  while (client[kPending] > 0 && client[kQueue][client[kPendingIdx]].servername === client[kServerName]) {
554
- const request = client[kQueue][client[kPendingIdx]++]
583
+ const request = client[kQueue].splice(client[kPendingIdx], 1)[0]
555
584
  util.errorRequest(client, request, err)
556
585
  }
557
586
  } else {
@@ -616,7 +645,7 @@ function _resume (client, sync) {
616
645
  return
617
646
  }
618
647
 
619
- if (client[kRunning] >= (getPipelining(client) || 1)) {
648
+ if (client[kRunning] >= (getMaxConcurrent(client) || 1)) {
620
649
  return
621
650
  }
622
651
 
@@ -38,6 +38,7 @@ class DispatcherBase extends Dispatcher {
38
38
  */
39
39
  get webSocketOptions () {
40
40
  return {
41
+ maxFragments: this[kWebSocketOptions].maxFragments ?? 131072,
41
42
  maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 // 128 MB default
42
43
  }
43
44
  }
@@ -147,7 +147,8 @@ class ProxyAgent extends DispatcherBase {
147
147
  factory: agentFactory,
148
148
  username: opts.username || username,
149
149
  password: opts.password || password,
150
- proxyTls: opts.proxyTls
150
+ proxyTls: opts.proxyTls,
151
+ requestTls: opts.requestTls
151
152
  })
152
153
  }
153
154
 
@@ -19,6 +19,7 @@ const kProxyAuth = Symbol('proxy auth')
19
19
  const kProxyProtocol = Symbol('proxy protocol')
20
20
  const kPools = Symbol('pools')
21
21
  const kConnector = Symbol('connector')
22
+ const kRequestTls = Symbol('request tls settings')
22
23
 
23
24
  // Static flag to ensure warning is only emitted once per process
24
25
  let experimentalWarningEmitted = false
@@ -53,6 +54,7 @@ class Socks5ProxyAgent extends DispatcherBase {
53
54
  this[kProxyUrl] = url
54
55
  this[kProxyHeaders] = options.headers || {}
55
56
  this[kProxyProtocol] = options.proxyTls ? 'https:' : 'http:'
57
+ this[kRequestTls] = options.requestTls
56
58
 
57
59
  // Extract auth from URL or options
58
60
  this[kProxyAuth] = {
@@ -205,9 +207,9 @@ class Socks5ProxyAgent extends DispatcherBase {
205
207
  }
206
208
  debug('upgrading to TLS')
207
209
  finalSocket = tls.connect({
210
+ ...this[kRequestTls],
208
211
  socket,
209
- servername: targetHost,
210
- ...connectOpts.tls || {}
212
+ servername: this[kRequestTls]?.servername || targetHost
211
213
  })
212
214
 
213
215
  const tlsReady = Promise.withResolvers()