@blue-soda/dsh-remote 0.5.5 → 0.5.7

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 (232) hide show
  1. package/README.md +2 -2
  2. package/cordis.patch.yml +30 -30
  3. package/dist/client.github.js +1 -1
  4. package/dist/client.js +1 -1
  5. package/dist/index.js +371 -80
  6. package/dist/index.js.map +4 -4
  7. package/dsh-plugin.json +41 -41
  8. package/package.json +165 -165
  9. package/dist/acp.d.ts +0 -75
  10. package/dist/acp.d.ts.map +0 -1
  11. package/dist/acp.js +0 -61
  12. package/dist/acp.js.map +0 -1
  13. package/dist/api-proxy-switch.d.ts +0 -33
  14. package/dist/api-proxy-switch.d.ts.map +0 -1
  15. package/dist/api-proxy-switch.js +0 -109
  16. package/dist/api-proxy-switch.js.map +0 -1
  17. package/dist/atomic-file.d.ts +0 -16
  18. package/dist/atomic-file.d.ts.map +0 -1
  19. package/dist/atomic-file.js +0 -81
  20. package/dist/atomic-file.js.map +0 -1
  21. package/dist/bin/doctor.d.ts +0 -3
  22. package/dist/bin/doctor.d.ts.map +0 -1
  23. package/dist/bin/doctor.js +0 -13
  24. package/dist/bin/doctor.js.map +0 -1
  25. package/dist/cli.d.ts +0 -32
  26. package/dist/cli.d.ts.map +0 -1
  27. package/dist/cli.js +0 -289
  28. package/dist/cli.js.map +0 -1
  29. package/dist/client-runtime.d.ts +0 -375
  30. package/dist/client-runtime.d.ts.map +0 -1
  31. package/dist/client-runtime.js +0 -1768
  32. package/dist/client-runtime.js.map +0 -1
  33. package/dist/client-secure-transport.d.ts +0 -23
  34. package/dist/client-secure-transport.d.ts.map +0 -1
  35. package/dist/client-secure-transport.js +0 -144
  36. package/dist/client-secure-transport.js.map +0 -1
  37. package/dist/client-target-store.d.ts +0 -40
  38. package/dist/client-target-store.d.ts.map +0 -1
  39. package/dist/client-target-store.js +0 -79
  40. package/dist/client-target-store.js.map +0 -1
  41. package/dist/client.d.ts +0 -13
  42. package/dist/client.d.ts.map +0 -1
  43. package/dist/codex/app-server.d.ts +0 -71
  44. package/dist/codex/app-server.d.ts.map +0 -1
  45. package/dist/codex/app-server.js +0 -268
  46. package/dist/codex/app-server.js.map +0 -1
  47. package/dist/codex/codex-image-codec.d.ts +0 -7
  48. package/dist/codex/codex-image-codec.d.ts.map +0 -1
  49. package/dist/codex/codex-image-codec.js +0 -288
  50. package/dist/codex/codex-image-codec.js.map +0 -1
  51. package/dist/codex/domain.d.ts +0 -102
  52. package/dist/codex/domain.d.ts.map +0 -1
  53. package/dist/codex/domain.js +0 -1323
  54. package/dist/codex/domain.js.map +0 -1
  55. package/dist/codex/method-policy.d.ts +0 -338
  56. package/dist/codex/method-policy.d.ts.map +0 -1
  57. package/dist/codex/method-policy.js +0 -141
  58. package/dist/codex/method-policy.js.map +0 -1
  59. package/dist/codex/peer-bridge.d.ts +0 -57
  60. package/dist/codex/peer-bridge.d.ts.map +0 -1
  61. package/dist/codex/peer-bridge.js +0 -299
  62. package/dist/codex/peer-bridge.js.map +0 -1
  63. package/dist/codex/permissions.d.ts +0 -4
  64. package/dist/codex/permissions.d.ts.map +0 -1
  65. package/dist/codex/permissions.js +0 -21
  66. package/dist/codex/permissions.js.map +0 -1
  67. package/dist/codex/session-id.d.ts +0 -10
  68. package/dist/codex/session-id.d.ts.map +0 -1
  69. package/dist/codex/session-id.js +0 -15
  70. package/dist/codex/session-id.js.map +0 -1
  71. package/dist/codex/virtual-harness.d.ts +0 -209
  72. package/dist/codex/virtual-harness.d.ts.map +0 -1
  73. package/dist/codex/virtual-harness.js +0 -2650
  74. package/dist/codex/virtual-harness.js.map +0 -1
  75. package/dist/codex-workspace-bridge.d.ts +0 -108
  76. package/dist/codex-workspace-bridge.d.ts.map +0 -1
  77. package/dist/codex-workspace-bridge.js +0 -502
  78. package/dist/codex-workspace-bridge.js.map +0 -1
  79. package/dist/config.d.ts +0 -99
  80. package/dist/config.d.ts.map +0 -1
  81. package/dist/config.js +0 -127
  82. package/dist/config.js.map +0 -1
  83. package/dist/connection-controller.d.ts +0 -44
  84. package/dist/connection-controller.d.ts.map +0 -1
  85. package/dist/connection-controller.js +0 -185
  86. package/dist/connection-controller.js.map +0 -1
  87. package/dist/control-route.d.ts +0 -34
  88. package/dist/control-route.d.ts.map +0 -1
  89. package/dist/control-route.js +0 -136
  90. package/dist/control-route.js.map +0 -1
  91. package/dist/control-runtime.d.ts +0 -138
  92. package/dist/control-runtime.d.ts.map +0 -1
  93. package/dist/control-runtime.js +0 -543
  94. package/dist/control-runtime.js.map +0 -1
  95. package/dist/control-stream.d.ts +0 -67
  96. package/dist/control-stream.d.ts.map +0 -1
  97. package/dist/control-stream.js +0 -179
  98. package/dist/control-stream.js.map +0 -1
  99. package/dist/defaults.d.ts +0 -9
  100. package/dist/defaults.d.ts.map +0 -1
  101. package/dist/defaults.js +0 -9
  102. package/dist/defaults.js.map +0 -1
  103. package/dist/file-viewer-bridge.d.ts +0 -30
  104. package/dist/file-viewer-bridge.d.ts.map +0 -1
  105. package/dist/file-viewer-bridge.js +0 -128
  106. package/dist/file-viewer-bridge.js.map +0 -1
  107. package/dist/file-viewer-contract.d.ts +0 -5
  108. package/dist/file-viewer-contract.d.ts.map +0 -1
  109. package/dist/file-viewer-contract.js +0 -4
  110. package/dist/file-viewer-contract.js.map +0 -1
  111. package/dist/harness-api-bridge.d.ts +0 -81
  112. package/dist/harness-api-bridge.d.ts.map +0 -1
  113. package/dist/harness-api-bridge.js +0 -862
  114. package/dist/harness-api-bridge.js.map +0 -1
  115. package/dist/harness-api-history.d.ts +0 -3
  116. package/dist/harness-api-history.d.ts.map +0 -1
  117. package/dist/harness-api-history.js +0 -59
  118. package/dist/harness-api-history.js.map +0 -1
  119. package/dist/harness-remote-bridge.d.ts +0 -60
  120. package/dist/harness-remote-bridge.d.ts.map +0 -1
  121. package/dist/harness-remote-bridge.js +0 -540
  122. package/dist/harness-remote-bridge.js.map +0 -1
  123. package/dist/harness-version.d.ts +0 -23
  124. package/dist/harness-version.d.ts.map +0 -1
  125. package/dist/harness-version.js +0 -95
  126. package/dist/harness-version.js.map +0 -1
  127. package/dist/identity-store.d.ts +0 -45
  128. package/dist/identity-store.d.ts.map +0 -1
  129. package/dist/identity-store.js +0 -186
  130. package/dist/identity-store.js.map +0 -1
  131. package/dist/ids.d.ts +0 -2
  132. package/dist/ids.d.ts.map +0 -1
  133. package/dist/ids.js +0 -17
  134. package/dist/ids.js.map +0 -1
  135. package/dist/index.d.ts +0 -78
  136. package/dist/index.d.ts.map +0 -1
  137. package/dist/logging.d.ts +0 -18
  138. package/dist/logging.d.ts.map +0 -1
  139. package/dist/logging.js +0 -30
  140. package/dist/logging.js.map +0 -1
  141. package/dist/loopback-host.d.ts +0 -20
  142. package/dist/loopback-host.d.ts.map +0 -1
  143. package/dist/loopback-host.js +0 -245
  144. package/dist/loopback-host.js.map +0 -1
  145. package/dist/loopback-preview.d.ts +0 -19
  146. package/dist/loopback-preview.d.ts.map +0 -1
  147. package/dist/loopback-preview.js +0 -232
  148. package/dist/loopback-preview.js.map +0 -1
  149. package/dist/native-rtc-helper.d.ts +0 -14
  150. package/dist/native-rtc-helper.d.ts.map +0 -1
  151. package/dist/native-rtc-helper.js +0 -575
  152. package/dist/native-rtc-helper.js.map +0 -1
  153. package/dist/remote-api-proxy.d.ts +0 -17
  154. package/dist/remote-api-proxy.d.ts.map +0 -1
  155. package/dist/remote-api-proxy.js +0 -451
  156. package/dist/remote-api-proxy.js.map +0 -1
  157. package/dist/remote-directory-browser.d.ts +0 -16
  158. package/dist/remote-directory-browser.d.ts.map +0 -1
  159. package/dist/remote-directory-browser.js +0 -48
  160. package/dist/remote-directory-browser.js.map +0 -1
  161. package/dist/remote-file-content-provider.d.ts +0 -48
  162. package/dist/remote-file-content-provider.d.ts.map +0 -1
  163. package/dist/remote-file-content-provider.js +0 -93
  164. package/dist/remote-file-content-provider.js.map +0 -1
  165. package/dist/remote-typert-gateway.d.ts +0 -20
  166. package/dist/remote-typert-gateway.d.ts.map +0 -1
  167. package/dist/remote-typert-gateway.js +0 -334
  168. package/dist/remote-typert-gateway.js.map +0 -1
  169. package/dist/rpc-binary-attachments.d.ts +0 -33
  170. package/dist/rpc-binary-attachments.d.ts.map +0 -1
  171. package/dist/rpc-binary-attachments.js +0 -142
  172. package/dist/rpc-binary-attachments.js.map +0 -1
  173. package/dist/rpc-router.d.ts +0 -31
  174. package/dist/rpc-router.d.ts.map +0 -1
  175. package/dist/rpc-router.js +0 -201
  176. package/dist/rpc-router.js.map +0 -1
  177. package/dist/safe-error.d.ts +0 -8
  178. package/dist/safe-error.d.ts.map +0 -1
  179. package/dist/safe-error.js +0 -20
  180. package/dist/safe-error.js.map +0 -1
  181. package/dist/server-api.d.ts +0 -109
  182. package/dist/server-api.d.ts.map +0 -1
  183. package/dist/server-api.js +0 -520
  184. package/dist/server-api.js.map +0 -1
  185. package/dist/server-connection.d.ts +0 -79
  186. package/dist/server-connection.d.ts.map +0 -1
  187. package/dist/server-connection.js +0 -1023
  188. package/dist/server-connection.js.map +0 -1
  189. package/dist/server-credentials.d.ts +0 -55
  190. package/dist/server-credentials.d.ts.map +0 -1
  191. package/dist/server-credentials.js +0 -108
  192. package/dist/server-credentials.js.map +0 -1
  193. package/dist/service.d.ts +0 -151
  194. package/dist/service.d.ts.map +0 -1
  195. package/dist/service.js +0 -448
  196. package/dist/service.js.map +0 -1
  197. package/dist/session-format-compat.d.ts +0 -3
  198. package/dist/session-format-compat.d.ts.map +0 -1
  199. package/dist/session-format-compat.js +0 -183
  200. package/dist/session-format-compat.js.map +0 -1
  201. package/dist/status-stream.d.ts +0 -48
  202. package/dist/status-stream.d.ts.map +0 -1
  203. package/dist/status-stream.js +0 -133
  204. package/dist/status-stream.js.map +0 -1
  205. package/dist/terminal-policy.d.ts +0 -21
  206. package/dist/terminal-policy.d.ts.map +0 -1
  207. package/dist/terminal-policy.js +0 -68
  208. package/dist/terminal-policy.js.map +0 -1
  209. package/dist/tui-command.d.ts +0 -13
  210. package/dist/tui-command.d.ts.map +0 -1
  211. package/dist/tui-command.js +0 -392
  212. package/dist/tui-command.js.map +0 -1
  213. package/dist/typert-gateway-contract.d.ts +0 -59
  214. package/dist/typert-gateway-contract.d.ts.map +0 -1
  215. package/dist/typert-gateway-contract.js +0 -2
  216. package/dist/typert-gateway-contract.js.map +0 -1
  217. package/dist/typert-gateway-switch.d.ts +0 -85
  218. package/dist/typert-gateway-switch.d.ts.map +0 -1
  219. package/dist/typert-gateway-switch.js +0 -448
  220. package/dist/typert-gateway-switch.js.map +0 -1
  221. package/dist/types.d.ts +0 -20
  222. package/dist/types.d.ts.map +0 -1
  223. package/dist/types.js +0 -2
  224. package/dist/types.js.map +0 -1
  225. package/dist/version.d.ts +0 -12
  226. package/dist/version.d.ts.map +0 -1
  227. package/dist/version.js +0 -12
  228. package/dist/version.js.map +0 -1
  229. package/dist/werift-rtc.d.ts +0 -121
  230. package/dist/werift-rtc.d.ts.map +0 -1
  231. package/dist/werift-rtc.js +0 -490
  232. package/dist/werift-rtc.js.map +0 -1
package/dist/index.js CHANGED
@@ -13521,7 +13521,7 @@ function normalizeServerUrl(value) {
13521
13521
  }
13522
13522
 
13523
13523
  // src/version.ts
13524
- var PLUGIN_VERSION = "0.5.5";
13524
+ var PLUGIN_VERSION = "0.5.7";
13525
13525
 
13526
13526
  // src/server-api.ts
13527
13527
  var ENABLED_QR_PROVIDERS = ["github"];
@@ -13998,6 +13998,7 @@ function requireRecord(value, name2) {
13998
13998
 
13999
13999
  // src/server-connection.ts
14000
14000
  var DEFAULT_WEBRTC_NEGOTIATE_TIMEOUT_MS = 12e3;
14001
+ var CLOSE_DRAIN_TIMEOUT_MS = 5e3;
14001
14002
  var HostServerConnection = class {
14002
14003
  constructor(config, identity, identities, api, connections, logger, createWebSocket = (url) => new WebSocket(url), rtcFactoryProvider, hostCapabilities = () => ["harness.api.v1"], harnessVersion) {
14003
14004
  this.config = config;
@@ -14041,7 +14042,7 @@ var HostServerConnection = class {
14041
14042
  }
14042
14043
  if (this.resumeQueued) return;
14043
14044
  this.resumeQueued = true;
14044
- void this.running.finally(() => {
14045
+ void this.running.catch(() => void 0).finally(() => {
14045
14046
  this.resumeQueued = false;
14046
14047
  if (!this.stopped) this.start();
14047
14048
  });
@@ -14077,7 +14078,7 @@ var HostServerConnection = class {
14077
14078
  this.reconnectRequested = true;
14078
14079
  this.retryWake?.();
14079
14080
  this.socket?.close(4e3, "manual reconnect");
14080
- void this.running.finally(() => {
14081
+ void this.running.catch(() => void 0).finally(() => {
14081
14082
  if (!this.reconnectRequested || this.stopped) return;
14082
14083
  this.reconnectRequested = false;
14083
14084
  this.start();
@@ -14107,7 +14108,7 @@ var HostServerConnection = class {
14107
14108
  ...connectionFailureDetail(error)
14108
14109
  });
14109
14110
  if (TERMINAL_AUTH_ERRORS.has(code)) {
14110
- this.logger.warn(code === "CONNECTION_REPLACED" ? "Another instance is using this Host identity. Standing by: retrying in five minutes, and taking over as soon as it leaves." : code === "SERVER_CREDENTIALS_BUSY" ? "Credential refresh is locked. Stop other instances; after a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again." : "Host authorization failed. Run /remote login or authorize this Host again in Remote settings.");
14111
+ this.logger.warn(code === "CONNECTION_REPLACED" ? "Another instance is using this Host identity. Standing by: retrying in five minutes, and taking over as soon as it leaves." : code === "SERVER_CREDENTIALS_BUSY" ? "Credential refresh is locked. Stop other instances; after a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again." : code === "CONTROL_DISABLED" ? 'This device is not accepting control. Turn on "Allow control of this device" to reconnect.' : "Host authorization failed. Run /remote login or authorize this Host again in Remote settings.");
14111
14112
  }
14112
14113
  if (code === "DEVICE_REVOKED" || code === "AUTH_INVALID") {
14113
14114
  try {
@@ -14166,9 +14167,25 @@ var HostServerConnection = class {
14166
14167
  });
14167
14168
  };
14168
14169
  socket.onmessage = (event) => {
14170
+ let frame;
14171
+ try {
14172
+ frame = decodeControl(event.data, this.controlFrameLimits);
14173
+ } catch (error) {
14174
+ this.controlFrameFailed(socket, error);
14175
+ return;
14176
+ }
14177
+ this.lastActiveAt = Date.now();
14178
+ if (acknowledged && (frame.type === "ping" || frame.type === "pong")) {
14179
+ if (frame.type === "pong") return;
14180
+ const nonce = objectValue(frame.payload, "nonce");
14181
+ if (typeof nonce !== "string") {
14182
+ this.controlFrameFailed(socket, new ControlConnectionError("INVALID_MESSAGE", "Control ping has no nonce."));
14183
+ return;
14184
+ }
14185
+ this.sendControl("pong", { nonce });
14186
+ return;
14187
+ }
14169
14188
  messageQueue = messageQueue.then(async () => {
14170
- const frame = decodeControl(event.data, this.controlFrameLimits);
14171
- this.lastActiveAt = Date.now();
14172
14189
  if (frame.type === "hello.ack") {
14173
14190
  const payload = requireHelloAck(frame.payload);
14174
14191
  this.authRecoveryAttempted = false;
@@ -14193,13 +14210,7 @@ var HostServerConnection = class {
14193
14210
  if (!acknowledged) throw new ControlConnectionError("INVALID_MESSAGE", "Server sent a frame before hello.ack.");
14194
14211
  await this.handleFrame(frame);
14195
14212
  }).catch((error) => {
14196
- const code = errorCode2(error);
14197
- this.terminalError = code;
14198
- this.logger.error("server control frame failed", {
14199
- code,
14200
- reason: diagnosticReason(error)
14201
- });
14202
- socket.close(4008, "invalid control frame");
14213
+ this.controlFrameFailed(socket, error);
14203
14214
  });
14204
14215
  };
14205
14216
  socket.onerror = (event) => {
@@ -14209,7 +14220,7 @@ var HostServerConnection = class {
14209
14220
  };
14210
14221
  socket.onclose = (event) => {
14211
14222
  const close = async () => {
14212
- await messageQueue.catch(() => void 0);
14223
+ await drainWithin(messageQueue, CLOSE_DRAIN_TIMEOUT_MS);
14213
14224
  if (this.stopped) {
14214
14225
  finish();
14215
14226
  return;
@@ -14237,13 +14248,23 @@ var HostServerConnection = class {
14237
14248
  finish(new ControlConnectionError("DEVICE_REVOKED", "The Server revoked this Host device."));
14238
14249
  return;
14239
14250
  }
14240
- if (acknowledged) this.terminalError = closeCode(event.code);
14241
- finish(acknowledged ? void 0 : new ControlConnectionError(closeCode(event.code), event.reason || "Server control connection closed."));
14251
+ if (acknowledged) this.terminalError = closeCode(event.code, event.reason);
14252
+ finish(acknowledged ? void 0 : new ControlConnectionError(closeCode(event.code, event.reason), event.reason || "Server control connection closed."));
14242
14253
  };
14243
14254
  void close();
14244
14255
  };
14245
14256
  });
14246
14257
  }
14258
+ /** Record a frame failure and close the socket, so the run retries from a clean state. */
14259
+ controlFrameFailed(socket, error) {
14260
+ const code = errorCode2(error);
14261
+ this.terminalError = code;
14262
+ this.logger.error("server control frame failed", {
14263
+ code,
14264
+ reason: diagnosticReason(error)
14265
+ });
14266
+ socket.close(4008, "invalid control frame");
14267
+ }
14247
14268
  async handleFrame(frame) {
14248
14269
  if (frame.type === "ping") {
14249
14270
  const nonce = objectValue(frame.payload, "nonce");
@@ -14705,10 +14726,25 @@ var HostServerConnection = class {
14705
14726
  }
14706
14727
  };
14707
14728
  var REPLACED_FIRST_RETRY_MS = 65e3;
14729
+ async function drainWithin(promise, ms) {
14730
+ let timer;
14731
+ try {
14732
+ await Promise.race([
14733
+ promise.catch(() => void 0),
14734
+ new Promise((resolve4) => {
14735
+ timer = setTimeout(resolve4, ms);
14736
+ timer.unref?.();
14737
+ })
14738
+ ]);
14739
+ } finally {
14740
+ if (timer !== void 0) clearTimeout(timer);
14741
+ }
14742
+ }
14708
14743
  var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set([
14709
14744
  "SERVER_CREDENTIALS_BUSY",
14710
14745
  "ACCOUNT_AUTH_REQUIRED",
14711
14746
  "AUTH_INVALID",
14747
+ "CONTROL_DISABLED",
14712
14748
  "DEVICE_OWNERSHIP_REQUIRED",
14713
14749
  "DEVICE_REVOKED",
14714
14750
  "TOKEN_EXPIRED"
@@ -14926,14 +14962,25 @@ function errorCode2(error) {
14926
14962
  function isRetryable(error) {
14927
14963
  return !TERMINAL_AUTH_ERRORS.has(errorCode2(error)) && (!(error instanceof ServerApiError) || error.retryable);
14928
14964
  }
14929
- function closeCode(code) {
14965
+ function closeCode(code, reason) {
14930
14966
  if (code === 4002) return "AUTH_INVALID";
14931
14967
  if (code === 4003) return "CONNECTION_REPLACED";
14932
14968
  if (code === 4004) return "DEVICE_REVOKED";
14933
14969
  if (code === 4007) return "RATE_LIMITED";
14934
14970
  if (code === 4011) return "UNSUPPORTED_VERSION";
14971
+ if (code === 4001 && reason !== void 0 && REJECTED_HELLO_CODES.has(reason)) return reason;
14935
14972
  return "CONNECTION_FAILED";
14936
14973
  }
14974
+ var REJECTED_HELLO_CODES = /* @__PURE__ */ new Set([
14975
+ "AUTH_REQUIRED",
14976
+ "AUTH_INVALID",
14977
+ "TOKEN_EXPIRED",
14978
+ "DEVICE_REVOKED",
14979
+ "CONTROL_DISABLED",
14980
+ "MEMBERSHIP_REQUIRED",
14981
+ "UNSUPPORTED_VERSION",
14982
+ "RATE_LIMITED"
14983
+ ]);
14937
14984
 
14938
14985
  // src/loopback-preview.ts
14939
14986
  import { randomBytes as randomBytes7, randomUUID } from "node:crypto";
@@ -15512,7 +15559,7 @@ var CodexRemoteClient = class {
15512
15559
  if (!isRecord(result) || !Array.isArray(result.data))
15513
15560
  throw invalidResponse("thread list");
15514
15561
  return {
15515
- sessions: result.data.map(projectCodexThread).filter((value) => value !== void 0),
15562
+ sessions: uniqueCodexThreadRows(result.data).map(projectCodexThread).filter((value) => value !== void 0),
15516
15563
  ...typeof result.nextCursor === "string" ? { nextCursor: result.nextCursor } : {}
15517
15564
  };
15518
15565
  }
@@ -15666,6 +15713,33 @@ function projectCodexThread(value) {
15666
15713
  ...typeof value.isPinned === "boolean" ? { pinned: value.isPinned } : {}
15667
15714
  };
15668
15715
  }
15716
+ var THREAD_ID_KEY = /^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/u;
15717
+ var LEGACY_ROW_KEY = /^([A-Za-z0-9][A-Za-z0-9._-]{0,255})~[^~]{0,64}~[0-9]{1,9}$/u;
15718
+ function codexThreadIdFromKey(key) {
15719
+ if (THREAD_ID_KEY.test(key))
15720
+ return key;
15721
+ return LEGACY_ROW_KEY.exec(key)?.[1];
15722
+ }
15723
+ function rowFreshness(row) {
15724
+ for (const field of ["updatedAt", "createdAt"]) {
15725
+ const value = row[field];
15726
+ if (typeof value === "number" && Number.isFinite(value))
15727
+ return value;
15728
+ }
15729
+ return 0;
15730
+ }
15731
+ function uniqueCodexThreadRows(values) {
15732
+ const byThread = /* @__PURE__ */ new Map();
15733
+ for (const value of values) {
15734
+ if (!isRecord(value) || typeof value.id !== "string" || value.id.length === 0)
15735
+ continue;
15736
+ const threadId = codexThreadIdFromKey(value.id) ?? value.id;
15737
+ const current = byThread.get(threadId);
15738
+ if (current === void 0 || rowFreshness(value) > rowFreshness(current))
15739
+ byThread.set(threadId, value);
15740
+ }
15741
+ return [...byThread.values()];
15742
+ }
15669
15743
  function deriveCodexCwdWorkspaces(values) {
15670
15744
  const byPath = /* @__PURE__ */ new Map();
15671
15745
  const usedIds = /* @__PURE__ */ new Set();
@@ -16866,7 +16940,7 @@ var RemoteHarnessApiProxy = class {
16866
16940
  if (committed.transferId !== transferId || committed.totalBytes <= 0 || committed.totalBytes > MAX_HARNESS_API_TRANSFER_BYTES || committed.totalChunks !== Math.ceil(committed.totalBytes / HARNESS_API_TRANSFER_CHUNK_BYTES)) {
16867
16941
  throw new Error("The remote Host returned an invalid Harness API transfer descriptor.");
16868
16942
  }
16869
- const responseBytes = new Uint8Array(committed.totalBytes);
16943
+ const responseBytes2 = new Uint8Array(committed.totalBytes);
16870
16944
  let offset = 0;
16871
16945
  for (let index = 0; index < committed.totalChunks; index += 1) {
16872
16946
  const result = await this.client.rpc(
@@ -16882,10 +16956,10 @@ var RemoteHarnessApiProxy = class {
16882
16956
  if (chunk.byteLength !== expectedBytes) {
16883
16957
  throw new Error("The remote Host returned an invalid Harness API transfer chunk.");
16884
16958
  }
16885
- responseBytes.set(chunk, offset);
16959
+ responseBytes2.set(chunk, offset);
16886
16960
  offset += chunk.byteLength;
16887
16961
  }
16888
- return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(responseBytes));
16962
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(responseBytes2));
16889
16963
  } finally {
16890
16964
  if (opened) {
16891
16965
  await this.client.rpc("harness.api.transfer.close", { transferId }).catch(() => void 0);
@@ -17360,7 +17434,7 @@ var RemoteTypertGateway2 = class {
17360
17434
  if (committed.transferId !== transferId || committed.totalBytes <= 0 || committed.totalBytes > MAX_HARNESS_API_TRANSFER_BYTES || committed.totalChunks !== Math.ceil(committed.totalBytes / HARNESS_API_TRANSFER_CHUNK_BYTES)) {
17361
17435
  throw new Error("The remote Host returned an invalid Harness Remote transfer descriptor.");
17362
17436
  }
17363
- const responseBytes = new Uint8Array(committed.totalBytes);
17437
+ const responseBytes2 = new Uint8Array(committed.totalBytes);
17364
17438
  let offset = 0;
17365
17439
  for (let index = 0; index < committed.totalChunks; index += 1) {
17366
17440
  const result = await this.client.rpc(
@@ -17376,10 +17450,10 @@ var RemoteTypertGateway2 = class {
17376
17450
  if (chunk.byteLength !== expectedBytes) {
17377
17451
  throw new Error("The remote Host returned an invalid Harness Remote transfer chunk.");
17378
17452
  }
17379
- responseBytes.set(chunk, offset);
17453
+ responseBytes2.set(chunk, offset);
17380
17454
  offset += chunk.byteLength;
17381
17455
  }
17382
- return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(responseBytes));
17456
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(responseBytes2));
17383
17457
  } finally {
17384
17458
  if (opened) await this.client.rpc("harness.remote.transfer.close", { transferId }).catch(() => void 0);
17385
17459
  }
@@ -17540,6 +17614,17 @@ function record(value) {
17540
17614
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
17541
17615
  }
17542
17616
 
17617
+ // src/codex/session-id.ts
17618
+ var SESSION_PREFIX = "codex:";
17619
+ function parseCodexSessionId(value) {
17620
+ if (typeof value !== "string" || !value.startsWith(SESSION_PREFIX)) return void 0;
17621
+ const threadId = codexThreadIdFromKey(value.slice(SESSION_PREFIX.length));
17622
+ return threadId === void 0 ? void 0 : { sessionId: value, threadId };
17623
+ }
17624
+ function resolveCodexThreadKey(key) {
17625
+ return codexThreadIdFromKey(key) ?? key;
17626
+ }
17627
+
17543
17628
  // src/codex/method-policy.ts
17544
17629
  var id2 = external_exports.string().min(1).max(256);
17545
17630
  var cursor = external_exports.string().min(1).max(4096).nullable().optional();
@@ -19411,7 +19496,7 @@ async function loadCatalog(client, signal, pendingThreads) {
19411
19496
  updatedAt: workspace.updatedAt
19412
19497
  }));
19413
19498
  }
19414
- const projected = threads.map((thread) => {
19499
+ const projected = uniqueCodexThreadRows(threads).map((thread) => {
19415
19500
  const session = projectCodexThread(thread);
19416
19501
  return session === void 0 ? void 0 : { thread, session };
19417
19502
  }).filter((value) => value !== void 0);
@@ -20121,10 +20206,9 @@ function sessionIdFromAddress(address) {
20121
20206
  return requiredString(address.childSessionId, "childSessionId");
20122
20207
  }
20123
20208
  function nativeThreadId(sessionId) {
20124
- if (!sessionId.startsWith(CODEX_SESSION_PREFIX) || sessionId.length === CODEX_SESSION_PREFIX.length) {
20125
- throw new Error("The selected Session does not belong to CodeX.");
20126
- }
20127
- return sessionId.slice(CODEX_SESSION_PREFIX.length);
20209
+ const parsed = parseCodexSessionId(sessionId);
20210
+ if (parsed === void 0) throw new Error("The selected Session does not belong to CodeX.");
20211
+ return parsed.threadId;
20128
20212
  }
20129
20213
  function activeTurnId(value) {
20130
20214
  if (!Array.isArray(value)) return void 0;
@@ -24679,8 +24763,9 @@ var CodexPeerBridge = class {
24679
24763
  method: "thread/read",
24680
24764
  params: { threadId: params.threadId, includeTurns: false }
24681
24765
  });
24682
- this.streams.set(params.streamId, params.threadId);
24683
- return { opened: true, streamId: params.streamId, threadId: params.threadId };
24766
+ const threadId = resolveCodexThreadKey(params.threadId);
24767
+ this.streams.set(params.streamId, threadId);
24768
+ return { opened: true, streamId: params.streamId, threadId };
24684
24769
  }
24685
24770
  closeStream(input2) {
24686
24771
  const params = streamCloseSchema.parse(input2);
@@ -24759,22 +24844,22 @@ var CodexPeerBridge = class {
24759
24844
  });
24760
24845
  throw error;
24761
24846
  }
24762
- const responseBytes = new TextEncoder().encode(JSON.stringify(response));
24763
- if (responseBytes.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES) return { kind: "inline", response };
24764
- if (responseBytes.byteLength > MAX_CODEX_APP_TRANSFER_BYTES) {
24847
+ const responseBytes2 = new TextEncoder().encode(JSON.stringify(response));
24848
+ if (responseBytes2.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES) return { kind: "inline", response };
24849
+ if (responseBytes2.byteLength > MAX_CODEX_APP_TRANSFER_BYTES) {
24765
24850
  throw new RpcError("RESPONSE_TOO_LARGE", "The Codex response exceeds the bounded transfer limit.");
24766
24851
  }
24767
24852
  if (this.outgoingTransfers.size >= MAX_ACTIVE_TRANSFERS) {
24768
24853
  throw new RpcError("RATE_LIMITED", "Too many Codex response transfers are active.", void 0, true);
24769
24854
  }
24770
- const totalChunks = Math.ceil(responseBytes.byteLength / CODEX_APP_TRANSFER_CHUNK_BYTES);
24855
+ const totalChunks = Math.ceil(responseBytes2.byteLength / CODEX_APP_TRANSFER_CHUNK_BYTES);
24771
24856
  this.outgoingTransfers.set(params.transferId, {
24772
- bytes: responseBytes,
24857
+ bytes: responseBytes2,
24773
24858
  totalChunks,
24774
24859
  nextIndex: 0,
24775
24860
  touchedAt: Date.now()
24776
24861
  });
24777
- return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes.byteLength, totalChunks };
24862
+ return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes2.byteLength, totalChunks };
24778
24863
  }
24779
24864
  readTransfer(input2) {
24780
24865
  this.requireOpen();
@@ -25026,9 +25111,7 @@ var CodexRemoteDomain = class {
25026
25111
  }));
25027
25112
  }
25028
25113
  if (isRecord12(call.params) && typeof call.params.threadId === "string") {
25029
- const rowKey = call.params.threadId;
25030
- const separator = rowKey.indexOf("~");
25031
- if (separator > 0) call.params.threadId = rowKey.slice(0, separator);
25114
+ call.params.threadId = resolveCodexThreadKey(call.params.threadId);
25032
25115
  }
25033
25116
  if (call.method === "thread/list") {
25034
25117
  const result = sanitizeThreadList(await this.callUpstream(call.method, call.params));
@@ -25038,10 +25121,7 @@ var CodexRemoteDomain = class {
25038
25121
  );
25039
25122
  return {
25040
25123
  ...authorized,
25041
- data: authorized.data.map((row, index) => ({
25042
- ...row,
25043
- id: `${String(row.id)}~${String(row.createdAt ?? "")}~${index}`
25044
- }))
25124
+ data: uniqueCodexThreadRows(authorized.data)
25045
25125
  };
25046
25126
  }
25047
25127
  if (call.method === "thread/start") {
@@ -26546,12 +26626,14 @@ var PluginControlRuntime = class {
26546
26626
  /** Consecutive reconciliations without a DSH session: DSH refreshes its grant, so one empty read is not a sign-out. */
26547
26627
  /** Guards against re-entry through configure(), which reads the settings view back. */
26548
26628
  reconcilingWithDeepSeek = false;
26629
+ /** The authorization exchange in flight; a settings poll must not start a second one. */
26630
+ deepseekAuthorization;
26549
26631
  /**
26550
26632
  * Keep this device's Server credentials in step with DSH's DeepSeek session - that session is the credential, and
26551
26633
  * the Server verifies it before issuing this device its own tokens.
26552
26634
  *
26553
26635
  * Three cases: DSH signed in and this device not authorized -> authorize; DSH signed out and this device authorized
26554
- * -> sign out; otherwise nothing. At most once per ten seconds, from the settings poll the panel already makes.
26636
+ * -> sign out; otherwise nothing. At most one exchange at a time, from the settings poll the panel already makes.
26555
26637
  */
26556
26638
  async reconcileWithDeepSeek() {
26557
26639
  if (this.deepseekSession === void 0 || this.reconcilingWithDeepSeek) return;
@@ -26560,19 +26642,20 @@ var PluginControlRuntime = class {
26560
26642
  const session = await this.deepseekSession.read();
26561
26643
  if (this.client !== void 0) this.client.hasDeepSeekSession = session !== void 0;
26562
26644
  const authorized = this.client === void 0 ? false : await this.client.hasServerAuthorization();
26563
- console.error(`[dsh-remote] reconcile ${JSON.stringify({ hasSession: session !== void 0, authorized })}`);
26564
26645
  if (session === void 0 && authorized) {
26565
26646
  await this.logout({ releaseDeepSeek: false });
26566
26647
  return;
26567
26648
  }
26568
26649
  if (session === void 0 || authorized) return;
26650
+ if (this.deepseekAuthorization !== void 0) return;
26569
26651
  if (this.client !== void 0) this.client.authorizingWithDeepSeek = true;
26570
- void (async () => {
26652
+ this.deepseekAuthorization = (async () => {
26571
26653
  try {
26572
26654
  await this.configure({ provider: "deepseek", serverUrl: this.config.serverUrl });
26573
26655
  } catch (error) {
26574
26656
  console.error(`[dsh-remote] reconcile failed ${JSON.stringify({ code: String(error?.code ?? "") })}`);
26575
26657
  } finally {
26658
+ this.deepseekAuthorization = void 0;
26576
26659
  if (this.client !== void 0) this.client.authorizingWithDeepSeek = false;
26577
26660
  }
26578
26661
  })();
@@ -26724,13 +26807,6 @@ function redact(value, key = "") {
26724
26807
  return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redact(child, childKey)]));
26725
26808
  }
26726
26809
 
26727
- // src/codex/session-id.ts
26728
- function parseCodexSessionId(value) {
26729
- if (typeof value !== "string") return void 0;
26730
- const match = /^codex:([A-Za-z0-9][A-Za-z0-9._-]{0,255})$/.exec(value);
26731
- return match === null ? void 0 : { sessionId: value, threadId: match[1] };
26732
- }
26733
-
26734
26810
  // src/terminal-policy.ts
26735
26811
  var TERMINAL_CALLS = /* @__PURE__ */ new Set([
26736
26812
  "terminal/environment",
@@ -26744,6 +26820,7 @@ var TERMINAL_CALLS = /* @__PURE__ */ new Set([
26744
26820
  ]);
26745
26821
  var TERMINAL_STREAMS = /* @__PURE__ */ new Set(["terminal/follow", "terminal/retain"]);
26746
26822
  var id3 = external_exports.string().regex(/^[A-Za-z0-9_:-]{1,256}$/);
26823
+ var sessionKey = external_exports.string().regex(/^[A-Za-z0-9_:~-]{1,512}$/);
26747
26824
  var TerminalPolicy = class {
26748
26825
  constructor(enabled, deviceId, owners) {
26749
26826
  this.enabled = enabled;
@@ -26758,7 +26835,7 @@ var TerminalPolicy = class {
26758
26835
  );
26759
26836
  const args = external_exports.object({ args: external_exports.record(external_exports.unknown()) }).strict().parse(payload).args;
26760
26837
  const rawSessionId = args.agentId ?? args.sessionId;
26761
- const sessionId = id3.parse(rawSessionId);
26838
+ const sessionId = sessionKey.parse(rawSessionId);
26762
26839
  const codexSession = parseCodexSessionId(sessionId);
26763
26840
  void codexSession;
26764
26841
  if (endpoint === "terminal/environment" || endpoint === "terminal/shells" || endpoint === "terminal/list") return {};
@@ -27391,8 +27468,8 @@ async function callHistoryWithRetry(callWithTimeout, payload, rpcId2, pageSizes)
27391
27468
  }
27392
27469
  throw new RpcError("INTERNAL_ERROR", "Failed to load session history with a fallback page size.");
27393
27470
  }
27394
- function sessionHistoryFallbackPageSizes(requestedMaxMessages) {
27395
- const requested = normalizeSessionHistoryPageSize(requestedMaxMessages);
27471
+ function sessionHistoryFallbackPageSizes(requestedMaxMessages2) {
27472
+ const requested = normalizeSessionHistoryPageSize(requestedMaxMessages2);
27396
27473
  const sizes = [];
27397
27474
  if (requested === void 0) {
27398
27475
  sizes.push(...SESSION_HISTORY_PAGE_SIZES);
@@ -27755,25 +27832,25 @@ var HarnessApiBridge = class {
27755
27832
  }
27756
27833
  const nativeRequest = callSchema2.parse(request);
27757
27834
  const response = await this.call(nativeRequest);
27758
- const responseBytes = new TextEncoder().encode(JSON.stringify(response));
27759
- if (responseBytes.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES2) {
27835
+ const responseBytes2 = new TextEncoder().encode(JSON.stringify(response));
27836
+ if (responseBytes2.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES2) {
27760
27837
  return { kind: "inline", response };
27761
27838
  }
27762
- if (responseBytes.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
27839
+ if (responseBytes2.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
27763
27840
  throw new RpcError("RESPONSE_TOO_LARGE", "The Harness API response exceeds the bounded transfer limit.");
27764
27841
  }
27765
27842
  this.pruneTransfers();
27766
27843
  if (this.outgoingTransfers.size >= MAX_ACTIVE_API_TRANSFERS) {
27767
27844
  throw new RpcError("RATE_LIMITED", "Too many Harness API response transfers are active.", void 0, true);
27768
27845
  }
27769
- const totalChunks = Math.ceil(responseBytes.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
27846
+ const totalChunks = Math.ceil(responseBytes2.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
27770
27847
  this.outgoingTransfers.set(params.transferId, {
27771
- bytes: responseBytes,
27848
+ bytes: responseBytes2,
27772
27849
  totalChunks,
27773
27850
  nextIndex: 0,
27774
27851
  touchedAt: Date.now()
27775
27852
  });
27776
- return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes.byteLength, totalChunks };
27853
+ return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes2.byteLength, totalChunks };
27777
27854
  }
27778
27855
  readTransfer(input2) {
27779
27856
  this.pruneTransfers();
@@ -28194,6 +28271,7 @@ function concatChunks2(chunks, totalBytes) {
28194
28271
  }
28195
28272
 
28196
28273
  // src/harness-remote-bridge.ts
28274
+ import { Buffer as Buffer4 } from "node:buffer";
28197
28275
  var endpointSchema = external_exports.string().min(1).max(128).regex(/^(?:\$events(?:\/result)?|[A-Za-z0-9_$.-]+\/[A-Za-z0-9_$.-]+)$/);
28198
28276
  var callSchema3 = external_exports.object({ endpoint: endpointSchema, payload: external_exports.unknown() }).strict();
28199
28277
  var streamOpenSchema3 = external_exports.object({
@@ -28241,6 +28319,13 @@ var directoryListSchema2 = external_exports.object({
28241
28319
  var MAX_ACTIVE_STREAMS2 = MAX_ALPHA_STREAMS_PER_CONNECTION;
28242
28320
  var MAX_ACTIVE_TRANSFERS2 = MAX_ACTIVE_TRANSFERS_PER_DIRECTION;
28243
28321
  var INLINE_TRANSFER_RESPONSE_BYTES3 = 2 * 1024 * 1024;
28322
+ var STREAM_FRAME_ENVELOPE_BYTES = 4 * 1024;
28323
+ var RESPONSE_ENVELOPE_BYTES = 8 * 1024;
28324
+ var HISTORY_PAGE_SIZES = [120, 80, 50, 30, 20, 12, 6, 3, 1];
28325
+ var MAX_HISTORY_PAGE_MESSAGES = 500;
28326
+ var REMOTE_HISTORY_CALL_ENDPOINTS = /* @__PURE__ */ new Set(["session/page", "session/history"]);
28327
+ var REMOTE_HISTORY_STREAM_ENDPOINTS = /* @__PURE__ */ new Set(["session/follow"]);
28328
+ var MAX_CACHED_HISTORY_PAGES = 64;
28244
28329
  var HARNESS_REMOTE_ALLOWLIST = [
28245
28330
  "$events",
28246
28331
  "$events/result",
@@ -28329,6 +28414,8 @@ var HarnessRemoteBridge = class {
28329
28414
  streams = /* @__PURE__ */ new Map();
28330
28415
  incomingTransfers = /* @__PURE__ */ new Map();
28331
28416
  outgoingTransfers = /* @__PURE__ */ new Map();
28417
+ /** Last page size that fit one secure message, per session, so probing is paid once. */
28418
+ historyPageSizes = /* @__PURE__ */ new Map();
28332
28419
  /**
28333
28420
  * Answer one remote Gateway call.
28334
28421
  *
@@ -28369,7 +28456,7 @@ var HarnessRemoteBridge = class {
28369
28456
  const signal = AbortSignal.timeout(6e4);
28370
28457
  try {
28371
28458
  const nativePayload = normalizeWorkspaceRequestPayload(params.endpoint, params.payload, this.harnessVersion);
28372
- const nativeResult = params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.gateway.dispatch(params.endpoint, nativePayload, signal);
28459
+ const nativeResult = REMOTE_HISTORY_CALL_ENDPOINTS.has(params.endpoint) ? await this.dispatchHistoryPage(params.endpoint, nativePayload, signal) : params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.gateway.dispatch(params.endpoint, nativePayload, signal);
28373
28460
  const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
28374
28461
  this.logger?.debug("harness remote call ok", {
28375
28462
  endpoint: params.endpoint,
@@ -28390,7 +28477,10 @@ var HarnessRemoteBridge = class {
28390
28477
  this.logger?.warn("harness remote call failed", {
28391
28478
  endpoint: params.endpoint,
28392
28479
  durationMs: Math.round(performance.now() - startedAt),
28393
- code: this.gateway.failure(error).code
28480
+ code: this.gateway.failure(error).code,
28481
+ // The code alone is what the client sees; the message is the only thing that says WHY the
28482
+ // Host threw (a strict-schema rejection, an oversized page, a session the gateway lost).
28483
+ message: error instanceof Error ? error.message : String(error)
28394
28484
  });
28395
28485
  throw error;
28396
28486
  }
@@ -28460,22 +28550,22 @@ var HarnessRemoteBridge = class {
28460
28550
  throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer does not contain a valid request.");
28461
28551
  }
28462
28552
  const response = await this.call(callSchema3.parse(request));
28463
- const responseBytes = new TextEncoder().encode(JSON.stringify(response));
28464
- if (responseBytes.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES3) return { kind: "inline", response };
28465
- if (responseBytes.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
28553
+ const responseBytes2 = new TextEncoder().encode(JSON.stringify(response));
28554
+ if (responseBytes2.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES3) return { kind: "inline", response };
28555
+ if (responseBytes2.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
28466
28556
  throw new RpcError("RESPONSE_TOO_LARGE", "The Harness Remote response exceeds the bounded transfer limit.");
28467
28557
  }
28468
28558
  if (this.outgoingTransfers.size >= MAX_ACTIVE_TRANSFERS2) {
28469
28559
  throw new RpcError("RATE_LIMITED", "Too many Harness Remote response transfers are active.", void 0, true);
28470
28560
  }
28471
- const totalChunks = Math.ceil(responseBytes.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
28561
+ const totalChunks = Math.ceil(responseBytes2.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
28472
28562
  this.outgoingTransfers.set(params.transferId, {
28473
- bytes: responseBytes,
28563
+ bytes: responseBytes2,
28474
28564
  totalChunks,
28475
28565
  nextIndex: 0,
28476
28566
  touchedAt: Date.now()
28477
28567
  });
28478
- return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes.byteLength, totalChunks };
28568
+ return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes2.byteLength, totalChunks };
28479
28569
  }
28480
28570
  readTransfer(input2) {
28481
28571
  this.pruneTransfers();
@@ -28493,7 +28583,7 @@ var HarnessRemoteBridge = class {
28493
28583
  return {
28494
28584
  transferId: params.transferId,
28495
28585
  index: params.index,
28496
- data: Buffer.from(transfer.bytes.subarray(start, end)).toString("base64")
28586
+ data: Buffer4.from(transfer.bytes.subarray(start, end)).toString("base64")
28497
28587
  };
28498
28588
  }
28499
28589
  closeTransfer(input2) {
@@ -28513,17 +28603,135 @@ var HarnessRemoteBridge = class {
28513
28603
  }
28514
28604
  const controller = new AbortController();
28515
28605
  this.streams.set(params.streamId, { controller });
28516
- let source;
28517
28606
  try {
28518
- source = (this.codexWorkspace === void 0 ? void 0 : await this.codexWorkspace.open(params.endpoint, params.payload, controller.signal)) ?? await this.gateway.open(params.endpoint, normalizeWorkspaceChangesPayload(params.endpoint, params.payload, this.harnessVersion), controller.signal);
28607
+ const nativePayload = normalizeWorkspaceChangesPayload(params.endpoint, params.payload, this.harnessVersion);
28608
+ if (REMOTE_HISTORY_STREAM_ENDPOINTS.has(params.endpoint)) {
28609
+ const opened = await this.openHistoryStream(params, nativePayload, controller.signal);
28610
+ void this.pump(params.streamId, opened.source, controller.signal, opened.frame);
28611
+ return { opened: true, streamId: params.streamId };
28612
+ }
28613
+ const source = (this.codexWorkspace === void 0 ? void 0 : await this.codexWorkspace.open(params.endpoint, params.payload, controller.signal)) ?? await this.gateway.open(params.endpoint, nativePayload, controller.signal);
28519
28614
  controller.signal.throwIfAborted();
28615
+ void this.pump(params.streamId, source, controller.signal);
28616
+ return { opened: true, streamId: params.streamId };
28520
28617
  } catch (error) {
28521
28618
  this.streams.delete(params.streamId);
28522
28619
  controller.abort();
28620
+ this.logger?.warn("harness remote stream open failed", {
28621
+ endpoint: params.endpoint,
28622
+ message: error instanceof Error ? error.message : String(error)
28623
+ });
28523
28624
  throw error;
28524
28625
  }
28525
- void this.pump(params.streamId, source, controller.signal);
28526
- return { opened: true, streamId: params.streamId };
28626
+ }
28627
+ /**
28628
+ * Open a history stream with its opening page chosen by byte budget.
28629
+ *
28630
+ * `session/follow` yields one snapshot page and then live events. The snapshot cannot be
28631
+ * retried after the stream is handed to the client, so each attempt is pulled here, measured,
28632
+ * and closed again when it would not fit one secure message. The accepted iterator continues
28633
+ * after that first frame, which the pump publishes before the live tail.
28634
+ * @param params - the validated stream-open request.
28635
+ * @param payload - the request payload after endpoint normalization.
28636
+ * @param signal - the stream's cancellation signal.
28637
+ * @returns the remaining source and the measured first frame.
28638
+ */
28639
+ async openHistoryStream(params, payload, signal) {
28640
+ const { key, sizes } = this.historyPageSizesFor(payload);
28641
+ for (const size of sizes) {
28642
+ const source = await this.gateway.open(params.endpoint, withHistoryWindow(payload, size), signal);
28643
+ const iterator = source[Symbol.asyncIterator]();
28644
+ let first;
28645
+ try {
28646
+ first = await iterator.next();
28647
+ } catch (error) {
28648
+ await iterator.return?.();
28649
+ throw error;
28650
+ }
28651
+ if (first.done === true) {
28652
+ await iterator.return?.();
28653
+ return { source: EMPTY_ASYNC_ITERABLE };
28654
+ }
28655
+ const frame = {
28656
+ streamId: params.streamId,
28657
+ hasValue: true,
28658
+ ...first.value === void 0 ? {} : { value: collectRpcAttachments(first.value) }
28659
+ };
28660
+ const bytes = streamFrameBytes(frame);
28661
+ this.logger?.debug("history stream attempt", { endpoint: params.endpoint, size, bytes });
28662
+ if (bytes <= MAX_SECURE_MESSAGE_BYTES - STREAM_FRAME_ENVELOPE_BYTES) {
28663
+ this.rememberHistoryPageSize(key, size);
28664
+ return { source: remainingIterable(iterator), frame };
28665
+ }
28666
+ await iterator.return?.();
28667
+ }
28668
+ throw new RpcError(
28669
+ "RESPONSE_TOO_LARGE",
28670
+ "The Harness history page is too large for the secure Remote channel. Request a smaller page.",
28671
+ { maxBytes: MAX_SECURE_MESSAGE_BYTES },
28672
+ true
28673
+ );
28674
+ }
28675
+ /**
28676
+ * Answer one unary history page with the largest window that still fits.
28677
+ *
28678
+ * DSH's client asks for 500 messages; a session whose only Turn is long answers that with
28679
+ * its whole log, which the 4 MiB secure-message limit cannot carry. Each page is measured
28680
+ * after attachment tagging (the form the wire carries) and the request steps down through
28681
+ * {@link HISTORY_PAGE_SIZES} until it fits. Only a size overflow retries: a business failure
28682
+ * is returned immediately.
28683
+ * @param endpoint - the history endpoint being called.
28684
+ * @param payload - the request payload after endpoint normalization.
28685
+ * @param signal - the call's timeout signal.
28686
+ * @returns the page, or `RESPONSE_TOO_LARGE` when even one message cannot cross.
28687
+ */
28688
+ async dispatchHistoryPage(endpoint, payload, signal) {
28689
+ const { key, sizes } = this.historyPageSizesFor(payload);
28690
+ for (const size of sizes) {
28691
+ const startedAt = performance.now();
28692
+ const result = await this.gateway.dispatch(endpoint, withHistoryWindow(payload, size), signal);
28693
+ if (!result.ok) return result;
28694
+ const value = result.value;
28695
+ const wire = collectRpcAttachments(result);
28696
+ const bytes = responseBytes(wire);
28697
+ this.logger?.debug("history page attempt", {
28698
+ endpoint,
28699
+ size,
28700
+ durationMs: Math.round(performance.now() - startedAt),
28701
+ ok: result.ok,
28702
+ records: Array.isArray(value?.records) ? value.records.length : void 0,
28703
+ hasMore: value?.hasMore,
28704
+ bytes
28705
+ });
28706
+ if (bytes <= MAX_SECURE_MESSAGE_BYTES - RESPONSE_ENVELOPE_BYTES) {
28707
+ this.rememberHistoryPageSize(key, size);
28708
+ return wire;
28709
+ }
28710
+ }
28711
+ return {
28712
+ ok: false,
28713
+ error: {
28714
+ code: "RESPONSE_TOO_LARGE",
28715
+ message: "The Harness history page is too large for the secure Remote channel. Request a smaller page.",
28716
+ details: { maxBytes: MAX_SECURE_MESSAGE_BYTES }
28717
+ }
28718
+ };
28719
+ }
28720
+ /** The ladder for one history request, starting from what last fit for this session. */
28721
+ historyPageSizesFor(payload) {
28722
+ const key = historyPageKey(payload);
28723
+ const requested = requestedMaxMessages(payload) ?? HISTORY_PAGE_SIZES[0];
28724
+ const remembered = this.historyPageSizes.get(key);
28725
+ const start = Math.max(1, Math.min(requested, MAX_HISTORY_PAGE_MESSAGES, remembered ?? requested));
28726
+ this.logger?.debug("history window", { key: key.slice(0, 120), requested, remembered, start });
28727
+ return { key, sizes: historyPageSizes(start) };
28728
+ }
28729
+ rememberHistoryPageSize(key, size) {
28730
+ if (this.historyPageSizes.size >= MAX_CACHED_HISTORY_PAGES && !this.historyPageSizes.has(key)) {
28731
+ const oldest = this.historyPageSizes.keys().next().value;
28732
+ if (oldest !== void 0) this.historyPageSizes.delete(oldest);
28733
+ }
28734
+ this.historyPageSizes.set(key, size);
28527
28735
  }
28528
28736
  closeStream(input2) {
28529
28737
  const params = streamCloseSchema3.parse(input2);
@@ -28539,6 +28747,7 @@ var HarnessRemoteBridge = class {
28539
28747
  this.streams.clear();
28540
28748
  this.incomingTransfers.clear();
28541
28749
  this.outgoingTransfers.clear();
28750
+ this.historyPageSizes.clear();
28542
28751
  for (const [, stream] of streams) stream.controller.abort(reason);
28543
28752
  await this.codexWorkspace?.closeAll();
28544
28753
  }
@@ -28550,17 +28759,28 @@ var HarnessRemoteBridge = class {
28550
28759
  throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not support Remote event transport.");
28551
28760
  }
28552
28761
  }
28553
- async pump(streamId, source, signal) {
28762
+ async pump(streamId, source, signal, initialFrame) {
28554
28763
  let reason = "completed";
28555
28764
  let failure2;
28556
28765
  try {
28766
+ if (initialFrame !== void 0 && !signal.aborted) await this.publish("harness.remote.frame", initialFrame);
28557
28767
  for await (const value of source) {
28558
28768
  if (signal.aborted) break;
28559
- await this.publish("harness.remote.frame", {
28769
+ const frame = {
28560
28770
  streamId,
28561
28771
  hasValue: true,
28562
28772
  ...value === void 0 ? {} : { value: collectRpcAttachments(value) }
28563
- });
28773
+ };
28774
+ if (streamFrameBytes(frame) > MAX_SECURE_MESSAGE_BYTES - STREAM_FRAME_ENVELOPE_BYTES) {
28775
+ failure2 = {
28776
+ code: "RESPONSE_TOO_LARGE",
28777
+ message: "The Harness stream frame is too large for the secure Remote channel.",
28778
+ details: { maxBytes: MAX_SECURE_MESSAGE_BYTES }
28779
+ };
28780
+ reason = "failed";
28781
+ break;
28782
+ }
28783
+ await this.publish("harness.remote.frame", frame);
28564
28784
  }
28565
28785
  if (signal.aborted) reason = "cancelled";
28566
28786
  } catch (error) {
@@ -28581,6 +28801,77 @@ var HarnessRemoteBridge = class {
28581
28801
  for (const [id4, transfer] of this.outgoingTransfers) if (transfer.touchedAt < cutoff) this.outgoingTransfers.delete(id4);
28582
28802
  }
28583
28803
  };
28804
+ function withHistoryWindow(payload, maxMessages) {
28805
+ if (!isRecord14(payload)) return payload;
28806
+ if (Array.isArray(payload.args)) {
28807
+ const [first, ...rest] = payload.args;
28808
+ return isRecord14(first) ? { ...payload, args: [windowedParams(first, maxMessages), ...rest] } : payload;
28809
+ }
28810
+ if (isRecord14(payload.args)) return { ...payload, args: windowedParams(payload.args, maxMessages) };
28811
+ return windowedParams(payload, maxMessages);
28812
+ }
28813
+ function windowedParams(params, maxMessages) {
28814
+ return isRecord14(params.request) ? { ...params, request: windowedRequest(params.request, maxMessages) } : windowedRequest(params, maxMessages);
28815
+ }
28816
+ function windowedRequest(request, maxMessages) {
28817
+ const turnWindow = isRecord14(request.turnWindow) ? request.turnWindow : void 0;
28818
+ const next = { ...request, maxMessages };
28819
+ if (turnWindow !== void 0) {
28820
+ next.turnWindow = typeof turnWindow.minMessages === "number" && Number.isFinite(turnWindow.minMessages) ? { ...turnWindow, minMessages: Math.min(turnWindow.minMessages, maxMessages) } : turnWindow;
28821
+ }
28822
+ return next;
28823
+ }
28824
+ function historyPageSizes(start) {
28825
+ const sizes = [start];
28826
+ for (const size of HISTORY_PAGE_SIZES) if (size < start) sizes.push(size);
28827
+ return sizes;
28828
+ }
28829
+ function historyRequestOf(payload) {
28830
+ let node = payload;
28831
+ for (let depth = 0; depth < 4; depth += 1) {
28832
+ if (!isRecord14(node)) return void 0;
28833
+ if (Array.isArray(node.args)) {
28834
+ node = node.args[0];
28835
+ continue;
28836
+ }
28837
+ if (isRecord14(node.args)) {
28838
+ node = node.args;
28839
+ continue;
28840
+ }
28841
+ if (isRecord14(node.request)) {
28842
+ node = node.request;
28843
+ continue;
28844
+ }
28845
+ return node;
28846
+ }
28847
+ return void 0;
28848
+ }
28849
+ function requestedMaxMessages(payload) {
28850
+ const value = historyRequestOf(payload)?.maxMessages;
28851
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
28852
+ }
28853
+ function historyPageKey(payload) {
28854
+ const request = historyRequestOf(payload);
28855
+ if (request === void 0) return "default";
28856
+ const address = request.address ?? request.request ?? request.threadId ?? request.sessionId;
28857
+ if (address === void 0) return "default";
28858
+ try {
28859
+ return JSON.stringify(address);
28860
+ } catch {
28861
+ return "default";
28862
+ }
28863
+ }
28864
+ function responseBytes(result) {
28865
+ return Buffer4.byteLength(JSON.stringify(result), "utf8");
28866
+ }
28867
+ function streamFrameBytes(frame) {
28868
+ return Buffer4.byteLength(JSON.stringify(frame), "utf8");
28869
+ }
28870
+ function remainingIterable(iterator) {
28871
+ return { [Symbol.asyncIterator]: () => iterator };
28872
+ }
28873
+ var EMPTY_ASYNC_ITERABLE = { async *[Symbol.asyncIterator]() {
28874
+ } };
28584
28875
  function normalizeWorkspaceChangesPayload(endpoint, payload, harnessVersion) {
28585
28876
  if (endpoint !== "workspaceFiles/changes" || !requiresWorkspaceChangePath(harnessVersion)) return payload;
28586
28877
  if (!isRecord14(payload) || !isRecord14(payload.args) || Object.hasOwn(payload.args, "path")) return payload;
@@ -28634,7 +28925,7 @@ async function dispatchCommandForHost(gateway, payload, signal, harnessVersion)
28634
28925
  );
28635
28926
  }
28636
28927
  function decodeCanonicalBase643(value) {
28637
- const bytes = Buffer.from(value, "base64");
28928
+ const bytes = Buffer4.from(value, "base64");
28638
28929
  if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
28639
28930
  return bytes;
28640
28931
  }