ds-harness-remote 0.5.13 → 0.5.15

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 (77) hide show
  1. package/dist/acp/peer-bridge.d.ts +1 -0
  2. package/dist/acp/peer-bridge.d.ts.map +1 -1
  3. package/dist/acp/peer-bridge.js +4 -1
  4. package/dist/acp/peer-bridge.js.map +1 -1
  5. package/dist/client/settings.d.ts.map +1 -1
  6. package/dist/client/settings.js +30 -11
  7. package/dist/client/settings.js.map +1 -1
  8. package/dist/client/shell.d.ts.map +1 -1
  9. package/dist/client/shell.js +8 -10
  10. package/dist/client/shell.js.map +1 -1
  11. package/dist/client/types.d.ts +1 -0
  12. package/dist/client/types.d.ts.map +1 -1
  13. package/dist/client/workspaces.d.ts.map +1 -1
  14. package/dist/client/workspaces.js +86 -20
  15. package/dist/client/workspaces.js.map +1 -1
  16. package/dist/client-connection.d.ts.map +1 -1
  17. package/dist/client-connection.js +14 -9
  18. package/dist/client-connection.js.map +1 -1
  19. package/dist/client-locale.d.ts +2 -1
  20. package/dist/client-locale.d.ts.map +1 -1
  21. package/dist/client-locale.js +4 -2
  22. package/dist/client-locale.js.map +1 -1
  23. package/dist/client-runtime.d.ts +3 -0
  24. package/dist/client-runtime.d.ts.map +1 -1
  25. package/dist/client-runtime.js +112 -65
  26. package/dist/client-runtime.js.map +1 -1
  27. package/dist/client.github.js +107 -55
  28. package/dist/client.js +107 -55
  29. package/dist/codex/history-preview.d.ts +3 -0
  30. package/dist/codex/history-preview.d.ts.map +1 -0
  31. package/dist/codex/history-preview.js +71 -0
  32. package/dist/codex/history-preview.js.map +1 -0
  33. package/dist/codex/peer-bridge.d.ts +1 -0
  34. package/dist/codex/peer-bridge.d.ts.map +1 -1
  35. package/dist/codex/peer-bridge.js +5 -1
  36. package/dist/codex/peer-bridge.js.map +1 -1
  37. package/dist/compact-history.d.ts +2 -0
  38. package/dist/compact-history.d.ts.map +1 -0
  39. package/dist/compact-history.js +71 -0
  40. package/dist/compact-history.js.map +1 -0
  41. package/dist/connection-controller.d.ts +10 -1
  42. package/dist/connection-controller.d.ts.map +1 -1
  43. package/dist/connection-controller.js +5 -3
  44. package/dist/connection-controller.js.map +1 -1
  45. package/dist/control-runtime.d.ts +12 -4
  46. package/dist/control-runtime.d.ts.map +1 -1
  47. package/dist/control-runtime.js +52 -8
  48. package/dist/control-runtime.js.map +1 -1
  49. package/dist/harness-api-bridge.d.ts +1 -0
  50. package/dist/harness-api-bridge.d.ts.map +1 -1
  51. package/dist/harness-api-bridge.js +8 -2
  52. package/dist/harness-api-bridge.js.map +1 -1
  53. package/dist/harness-remote-bridge.d.ts +3 -1
  54. package/dist/harness-remote-bridge.d.ts.map +1 -1
  55. package/dist/harness-remote-bridge.js +76 -6
  56. package/dist/harness-remote-bridge.js.map +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +610 -266
  59. package/dist/index.js.map +4 -4
  60. package/dist/plugin-route-bridge.d.ts +1 -1
  61. package/dist/plugin-route-bridge.d.ts.map +1 -1
  62. package/dist/plugin-route-bridge.js +1 -1
  63. package/dist/plugin-route-bridge.js.map +1 -1
  64. package/dist/remote-directory-browser.d.ts +6 -0
  65. package/dist/remote-directory-browser.d.ts.map +1 -1
  66. package/dist/remote-directory-browser.js +16 -1
  67. package/dist/remote-directory-browser.js.map +1 -1
  68. package/dist/rpc-router.d.ts.map +1 -1
  69. package/dist/rpc-router.js +11 -1
  70. package/dist/rpc-router.js.map +1 -1
  71. package/dist/server-connection.d.ts.map +1 -1
  72. package/dist/server-connection.js +12 -4
  73. package/dist/server-connection.js.map +1 -1
  74. package/dist/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/dsh-plugin.json +1 -1
  77. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -5586,10 +5586,10 @@ var util;
5586
5586
  return obj[e];
5587
5587
  });
5588
5588
  };
5589
- util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => {
5589
+ util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object3) => {
5590
5590
  const keys = [];
5591
- for (const key in object) {
5592
- if (Object.prototype.hasOwnProperty.call(object, key)) {
5591
+ for (const key in object3) {
5592
+ if (Object.prototype.hasOwnProperty.call(object3, key)) {
5593
5593
  keys.push(key);
5594
5594
  }
5595
5595
  }
@@ -8692,8 +8692,8 @@ var ZodSet = class _ZodSet extends ZodType {
8692
8692
  maxSize: { value: maxSize, message: errorUtil.toString(message) }
8693
8693
  });
8694
8694
  }
8695
- size(size, message) {
8696
- return this.min(size, message).max(size, message);
8695
+ size(size2, message) {
8696
+ return this.min(size2, message).max(size2, message);
8697
8697
  }
8698
8698
  nonempty(message) {
8699
8699
  return this.min(1, message);
@@ -9579,6 +9579,7 @@ var rpcMethods = [
9579
9579
  "workspace.upload.commit",
9580
9580
  "workspace.upload.abort",
9581
9581
  "harness.transport.describe",
9582
+ "remote.history.configure",
9582
9583
  "harness.api.call",
9583
9584
  "harness.api.transfer.open",
9584
9585
  "harness.api.transfer.chunk",
@@ -9943,8 +9944,8 @@ function createRpcRequest(method, params, id5) {
9943
9944
  function createRpcResponse(requestId, result) {
9944
9945
  return createMessage("rpc.response", { requestId, result });
9945
9946
  }
9946
- function createRpcError(requestId, code, message, details, retryable) {
9947
- return createMessage("rpc.error", { requestId, code, message, details, retryable });
9947
+ function createRpcError(requestId, code, message, details2, retryable) {
9948
+ return createMessage("rpc.error", { requestId, code, message, details: details2, retryable });
9948
9949
  }
9949
9950
  function createEvent(event, data2, options = {}) {
9950
9951
  return createMessage("event", { event, data: data2, ...options });
@@ -10099,10 +10100,10 @@ function remoteWorkspaceTypeAvailable(capabilities, workspaceTypes, id5, capabil
10099
10100
  var RemoteGatewayError = class extends Error {
10100
10101
  code;
10101
10102
  details;
10102
- constructor(code, message, details = {}, options) {
10103
+ constructor(code, message, details2 = {}, options) {
10103
10104
  super(message, options);
10104
10105
  this.code = code;
10105
- this.details = details;
10106
+ this.details = details2;
10106
10107
  this.name = "RemoteGatewayError";
10107
10108
  }
10108
10109
  };
@@ -10119,12 +10120,26 @@ var CODEX_IMAGE_MEDIA_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg"
10119
10120
  var DATA_IMAGE_URL = /^data:(image\/(?:png|jpeg|webp|gif));base64,([A-Za-z0-9+/]+={0,2})$/u;
10120
10121
  var CodexRemoteClient = class {
10121
10122
  core;
10122
- constructor(core) {
10123
+ historyPreview;
10124
+ constructor(core, historyPreview = false) {
10123
10125
  this.core = core;
10126
+ this.historyPreview = historyPreview;
10124
10127
  }
10125
10128
  /** Low-level allowlisted request used by the Desktop Web loopback facade. */
10126
- request(method, params, signal) {
10127
- const largeHistory = method === "dsh/sessionHistory" || method === "thread/read" && isRecord(params) && params.includeTurns === true;
10129
+ previewSupported = true;
10130
+ async request(method, params, signal) {
10131
+ if (method === "dsh/sessionHistory" && this.historyPreview && this.previewSupported) {
10132
+ try {
10133
+ return await this.core.rpc("codex.app.call", { method, params, previewHistory: true }, signal);
10134
+ } catch (error) {
10135
+ const code = error?.code;
10136
+ if (code !== "INVALID_MESSAGE" && code !== "RESPONSE_TOO_LARGE")
10137
+ throw error;
10138
+ if (code === "INVALID_MESSAGE")
10139
+ this.previewSupported = false;
10140
+ }
10141
+ }
10142
+ const largeHistory = method === "dsh/historyDetail" || method === "dsh/sessionHistory" || method === "thread/read" && isRecord(params) && params.includeTurns === true;
10128
10143
  return this.call(method, params, largeHistory || hasImageInput(params), signal);
10129
10144
  }
10130
10145
  async account(signal) {
@@ -11316,8 +11331,8 @@ function rcEndpoint(endpoint) {
11316
11331
  function success(value) {
11317
11332
  return { ok: true, value };
11318
11333
  }
11319
- function failure(code, message, details = {}) {
11320
- return { ok: false, error: { code, message, details } };
11334
+ function failure(code, message, details2 = {}) {
11335
+ return { ok: false, error: { code, message, details: details2 } };
11321
11336
  }
11322
11337
  function business(value) {
11323
11338
  if (isRecord3(value) && value.ok === false && isRecord3(value.error)) {
@@ -11333,8 +11348,8 @@ function business(value) {
11333
11348
  if (isRecord3(value) && value.ok === true) return { ok: true, value: value.value };
11334
11349
  return { ok: true, value };
11335
11350
  }
11336
- function fail(code, message, details = {}) {
11337
- return { ok: false, error: { code, message, details } };
11351
+ function fail(code, message, details2 = {}) {
11352
+ return { ok: false, error: { code, message, details: details2 } };
11338
11353
  }
11339
11354
  function failFrom(error) {
11340
11355
  const source = error instanceof Error ? error : new Error(String(error));
@@ -11476,12 +11491,12 @@ var AcpEventCarrier = class {
11476
11491
  } else if (event === "api-session/removed" && sessionId !== void 0) {
11477
11492
  this.broadcastRcHost({ type: "host/session-removed", sessionId });
11478
11493
  } else if (event === "api-session/added" && isRecord3(args[0])) {
11479
- const summary = args[0];
11494
+ const summary2 = args[0];
11480
11495
  this.broadcastRcHost({
11481
11496
  type: "host/session-added",
11482
- sessionId: summary.sessionId,
11483
- blank: summary.blank === true,
11484
- ...typeof summary.cwd === "string" ? { cwd: summary.cwd } : {}
11497
+ sessionId: summary2.sessionId,
11498
+ blank: summary2.blank === true,
11499
+ ...typeof summary2.cwd === "string" ? { cwd: summary2.cwd } : {}
11485
11500
  });
11486
11501
  }
11487
11502
  }
@@ -13311,10 +13326,10 @@ function normalizeProtocol(value) {
13311
13326
  return value.toLowerCase();
13312
13327
  }
13313
13328
  function relatedAddressSummary(address) {
13314
- const summary = summarizeAddress(address);
13329
+ const summary2 = summarizeAddress(address);
13315
13330
  return {
13316
- relatedAddressFamily: summary.addressFamily,
13317
- relatedAddressScope: summary.addressScope
13331
+ relatedAddressFamily: summary2.addressFamily,
13332
+ relatedAddressScope: summary2.addressScope
13318
13333
  };
13319
13334
  }
13320
13335
  function summarizeIpv4Scope(parts) {
@@ -13789,19 +13804,19 @@ var RtcDataChannelTransport = class {
13789
13804
  }
13790
13805
  }
13791
13806
  async handleIce(candidate) {
13792
- const summary = summarizeIceCandidate(candidate);
13807
+ const summary2 = summarizeIceCandidate(candidate);
13793
13808
  if (!this.remoteDescriptionSet) {
13794
- recordCandidate(this.remoteCandidateTelemetry, summary);
13795
- this.emitDiagnostic({ type: "remote-candidate", candidate: summary, action: "buffered", diagnostics: this.diagnostics() });
13809
+ recordCandidate(this.remoteCandidateTelemetry, summary2);
13810
+ this.emitDiagnostic({ type: "remote-candidate", candidate: summary2, action: "buffered", diagnostics: this.diagnostics() });
13796
13811
  this.remoteCandidates.push(candidate);
13797
13812
  return;
13798
13813
  }
13799
13814
  try {
13800
13815
  await this.pc.addIceCandidate(candidate);
13801
- recordCandidate(this.remoteCandidateTelemetry, summary);
13802
- this.emitDiagnostic({ type: "remote-candidate", candidate: summary, action: "added", diagnostics: this.diagnostics() });
13816
+ recordCandidate(this.remoteCandidateTelemetry, summary2);
13817
+ this.emitDiagnostic({ type: "remote-candidate", candidate: summary2, action: "added", diagnostics: this.diagnostics() });
13803
13818
  } catch {
13804
- this.emitDiagnostic({ type: "remote-candidate", candidate: summary, action: "ignored", diagnostics: this.diagnostics() });
13819
+ this.emitDiagnostic({ type: "remote-candidate", candidate: summary2, action: "ignored", diagnostics: this.diagnostics() });
13805
13820
  }
13806
13821
  }
13807
13822
  async flushRemoteCandidates() {
@@ -14203,19 +14218,19 @@ function isExplicitlyNonLanScope(scope2) {
14203
14218
  function increment(map, key) {
14204
14219
  map[key] = (map[key] ?? 0) + 1;
14205
14220
  }
14206
- function selectedPathTelemetry(details) {
14221
+ function selectedPathTelemetry(details2) {
14207
14222
  return {
14208
- ...details.transport === void 0 ? {} : { transport: details.transport },
14209
- ...details.mode === void 0 ? {} : { mode: details.mode },
14210
- ...details.relayUrl === void 0 ? {} : { relayUrl: details.relayUrl },
14211
- ...details.localCandidateType === void 0 ? {} : { localCandidateType: details.localCandidateType },
14212
- ...details.remoteCandidateType === void 0 ? {} : { remoteCandidateType: details.remoteCandidateType },
14213
- ...details.localAddressScope === void 0 ? {} : { localAddressScope: details.localAddressScope },
14214
- ...details.remoteAddressScope === void 0 ? {} : { remoteAddressScope: details.remoteAddressScope },
14215
- ...details.protocol === void 0 ? {} : { protocol: details.protocol },
14216
- ...details.relayProtocol === void 0 ? {} : { relayProtocol: details.relayProtocol },
14217
- ...details.currentRoundTripTimeMs === void 0 ? {} : { currentRoundTripTimeMs: details.currentRoundTripTimeMs },
14218
- ...details.availableOutgoingBitrate === void 0 ? {} : { availableOutgoingBitrate: details.availableOutgoingBitrate }
14223
+ ...details2.transport === void 0 ? {} : { transport: details2.transport },
14224
+ ...details2.mode === void 0 ? {} : { mode: details2.mode },
14225
+ ...details2.relayUrl === void 0 ? {} : { relayUrl: details2.relayUrl },
14226
+ ...details2.localCandidateType === void 0 ? {} : { localCandidateType: details2.localCandidateType },
14227
+ ...details2.remoteCandidateType === void 0 ? {} : { remoteCandidateType: details2.remoteCandidateType },
14228
+ ...details2.localAddressScope === void 0 ? {} : { localAddressScope: details2.localAddressScope },
14229
+ ...details2.remoteAddressScope === void 0 ? {} : { remoteAddressScope: details2.remoteAddressScope },
14230
+ ...details2.protocol === void 0 ? {} : { protocol: details2.protocol },
14231
+ ...details2.relayProtocol === void 0 ? {} : { relayProtocol: details2.relayProtocol },
14232
+ ...details2.currentRoundTripTimeMs === void 0 ? {} : { currentRoundTripTimeMs: details2.currentRoundTripTimeMs },
14233
+ ...details2.availableOutgoingBitrate === void 0 ? {} : { availableOutgoingBitrate: details2.availableOutgoingBitrate }
14219
14234
  };
14220
14235
  }
14221
14236
  function requireSdp(description) {
@@ -14309,11 +14324,11 @@ var AdaptiveTransport = class extends BaseTransport {
14309
14324
  };
14310
14325
  socket.onerror = () => {
14311
14326
  if (this.lifetime === lifetime)
14312
- this.failConnection(new Error(`AdaptiveTransport failed to connect to ${this.url}`));
14327
+ this.failConnection(new Error(`AdaptiveTransport failed to connect to ${this.url}`), { reason: "control-error" });
14313
14328
  };
14314
14329
  socket.onclose = (event) => {
14315
14330
  if (this.lifetime === lifetime)
14316
- this.failConnection(adaptiveControlCloseError(event));
14331
+ this.failConnection(adaptiveControlCloseError(event), { reason: "control-closed", closeCode: event.code });
14317
14332
  };
14318
14333
  });
14319
14334
  }
@@ -14483,7 +14498,7 @@ var AdaptiveTransport = class extends BaseTransport {
14483
14498
  if (frame.type === "ping")
14484
14499
  this.sendControl("pong", frame.payload);
14485
14500
  } catch (error) {
14486
- this.failConnection(error instanceof Error ? error : new Error("Invalid adaptive control frame"));
14501
+ this.failConnection(error instanceof Error ? error : new Error("Invalid adaptive control frame"), { reason: "invalid-control-frame" });
14487
14502
  }
14488
14503
  }
14489
14504
  handleRelay(payload) {
@@ -14596,8 +14611,9 @@ var AdaptiveTransport = class extends BaseTransport {
14596
14611
  this.emit(data2);
14597
14612
  });
14598
14613
  rtc.onClose(() => {
14599
- if (this.rtc === rtc && this.dataMode === "webrtc")
14600
- this.emitClose();
14614
+ if (this.rtc === rtc && this.dataMode === "webrtc") {
14615
+ this.failConnection(new Error("Active WebRTC channel closed"), { reason: "webrtc-closed" });
14616
+ }
14601
14617
  });
14602
14618
  try {
14603
14619
  await rtc.connect(signal);
@@ -14688,7 +14704,8 @@ var AdaptiveTransport = class extends BaseTransport {
14688
14704
  this.readyResolve = void 0;
14689
14705
  this.readyReject = void 0;
14690
14706
  }
14691
- failConnection(error) {
14707
+ failConnection(error, diagnostic) {
14708
+ const established = this.connectedAt !== void 0;
14692
14709
  this.clearHandshake();
14693
14710
  const reject = this.readyReject;
14694
14711
  this.readyResolve = void 0;
@@ -14707,6 +14724,12 @@ var AdaptiveTransport = class extends BaseTransport {
14707
14724
  reject?.(error);
14708
14725
  void rtc?.close();
14709
14726
  socket?.close();
14727
+ if (established && diagnostic !== void 0) {
14728
+ try {
14729
+ this.options.onDisconnect?.(diagnostic);
14730
+ } catch {
14731
+ }
14732
+ }
14710
14733
  if (socket !== void 0)
14711
14734
  this.emitClose();
14712
14735
  }
@@ -15686,11 +15709,11 @@ function bitLen(n) {
15686
15709
  return len;
15687
15710
  }
15688
15711
  var bitMask = (n) => (_1n << BigInt(n)) - _1n;
15689
- function _validateObject(object, fields, optFields = {}) {
15690
- if (!object || typeof object !== "object")
15712
+ function _validateObject(object3, fields, optFields = {}) {
15713
+ if (!object3 || typeof object3 !== "object")
15691
15714
  throw new Error("expected valid options object");
15692
15715
  function checkField(fieldName, expectedType, isOpt) {
15693
- const val = object[fieldName];
15716
+ const val = object3[fieldName];
15694
15717
  if (isOpt && val === void 0)
15695
15718
  return;
15696
15719
  const current = typeof val;
@@ -16786,18 +16809,18 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
16786
16809
  */
16787
16810
  toMontgomery(publicKey) {
16788
16811
  const { y } = Point.fromBytes(publicKey);
16789
- const size = lengths.publicKey;
16790
- const is25519 = size === 32;
16791
- if (!is25519 && size !== 57)
16812
+ const size2 = lengths.publicKey;
16813
+ const is25519 = size2 === 32;
16814
+ if (!is25519 && size2 !== 57)
16792
16815
  throw new Error("only defined for 25519 and 448");
16793
16816
  const u = is25519 ? Fp2.div(_1n4 + y, _1n4 - y) : Fp2.div(y - _1n4, y + _1n4);
16794
16817
  return Fp2.toBytes(u);
16795
16818
  },
16796
16819
  toMontgomerySecret(secretKey2) {
16797
- const size = lengths.secretKey;
16798
- _abytes2(secretKey2, size);
16799
- const hashed = cHash(secretKey2.subarray(0, size));
16800
- return adjustScalarBytes3(hashed).subarray(0, size);
16820
+ const size2 = lengths.secretKey;
16821
+ _abytes2(secretKey2, size2);
16822
+ const hashed = cHash(secretKey2.subarray(0, size2));
16823
+ return adjustScalarBytes3(hashed).subarray(0, size2);
16801
16824
  },
16802
16825
  /** @deprecated */
16803
16826
  randomPrivateKey: randomSecretKey,
@@ -18592,14 +18615,14 @@ var bitMask2 = (n) => {
18592
18615
  asafenumber(n, "n");
18593
18616
  return (_1n7 << BigInt(n)) - _1n7;
18594
18617
  };
18595
- function validateObject2(object, fields = {}, optFields = {}, title = "object") {
18596
- aobject2(object, title);
18618
+ function validateObject2(object3, fields = {}, optFields = {}, title = "object") {
18619
+ aobject2(object3, title);
18597
18620
  aobject2(fields, "fields");
18598
18621
  aobject2(optFields, "optFields");
18599
18622
  function checkField(fieldName, expectedType, isOpt) {
18600
18623
  const label = title === "object" ? `param "${String(fieldName)}"` : `"${title}.${String(fieldName)}"`;
18601
- const val = object[fieldName];
18602
- if (!Object.hasOwn(object, fieldName) && (isOpt ? val !== void 0 : expectedType !== "function")) {
18624
+ const val = object3[fieldName];
18625
+ if (!Object.hasOwn(object3, fieldName) && (isOpt ? val !== void 0 : expectedType !== "function")) {
18603
18626
  throw new TypeError(`${label} is invalid: expected own property`);
18604
18627
  }
18605
18628
  if (isOpt && val === void 0)
@@ -19110,24 +19133,24 @@ var pointWindowSizes2 = /* @__PURE__ */ new WeakMap();
19110
19133
  function getWindowSize(P) {
19111
19134
  return pointWindowSizes2.get(P) || 1;
19112
19135
  }
19113
- function oddMultiples(p, size) {
19136
+ function oddMultiples(p, size2) {
19114
19137
  const dbl = p.double();
19115
19138
  const t = [p];
19116
- for (let j = 1; j < size; j++)
19139
+ for (let j = 1; j < size2; j++)
19117
19140
  t.push(t[j - 1].add(dbl));
19118
19141
  return t;
19119
19142
  }
19120
19143
  function wnafDigits(n, W) {
19121
- const size = 2 ** W;
19122
- const half = size / 2;
19123
- const mask = BigInt(size - 1);
19144
+ const size2 = 2 ** W;
19145
+ const half = size2 / 2;
19146
+ const mask = BigInt(size2 - 1);
19124
19147
  const d = [];
19125
19148
  while (n > _0n9) {
19126
19149
  let w = 0;
19127
19150
  if (n & _1n9) {
19128
19151
  w = Number(n & mask);
19129
19152
  if (w >= half)
19130
- w -= size;
19153
+ w -= size2;
19131
19154
  n -= BigInt(w);
19132
19155
  }
19133
19156
  d.push(w);
@@ -19136,16 +19159,16 @@ function wnafDigits(n, W) {
19136
19159
  return d;
19137
19160
  }
19138
19161
  function signedWindowDigits(n, W, windows) {
19139
- const size = 2 ** W;
19140
- const half = size / 2;
19141
- const mask = BigInt(size - 1);
19162
+ const size2 = 2 ** W;
19163
+ const half = size2 / 2;
19164
+ const mask = BigInt(size2 - 1);
19142
19165
  const shiftBy = BigInt(W);
19143
19166
  const d = [];
19144
19167
  for (let w = 0; w < windows; w++) {
19145
19168
  let v = Number(n & mask);
19146
19169
  n >>= shiftBy;
19147
19170
  if (v > half) {
19148
- v -= size;
19171
+ v -= size2;
19149
19172
  n += _1n9;
19150
19173
  }
19151
19174
  d.push(v);
@@ -19316,11 +19339,11 @@ var ScalarMultiplier = class {
19316
19339
  */
19317
19340
  fixedWindowCT(point, n, bits) {
19318
19341
  const W = FW_WINDOW;
19319
- const size = 1 << W;
19342
+ const size2 = 1 << W;
19320
19343
  const mask = bitMask2(W);
19321
- const table = new Array(size);
19344
+ const table = new Array(size2);
19322
19345
  table[0] = this.ZERO;
19323
- for (let i = 1; i < size; i++)
19346
+ for (let i = 1; i < size2; i++)
19324
19347
  table[i] = table[i - 1].add(point);
19325
19348
  const windows = Math.ceil(bits / W);
19326
19349
  let acc = this.ZERO;
@@ -19330,7 +19353,7 @@ var ScalarMultiplier = class {
19330
19353
  acc = acc.double();
19331
19354
  const digit = Number(n >> BigInt(window2 * W) & mask);
19332
19355
  let sel = table[0];
19333
- for (let i = 1; i < size; i++)
19356
+ for (let i = 1; i < size2; i++)
19334
19357
  sel = i === digit ? table[i] : sel;
19335
19358
  acc = acc.add(sel);
19336
19359
  }
@@ -22260,16 +22283,24 @@ var ClientConnection = class {
22260
22283
  this.context.connectionProgress = { runId: progressRunId, targetDeviceId, phase: "checking-host" };
22261
22284
  const identity2 = this.context.requireIdentity();
22262
22285
  let client;
22286
+ const onAbort = () => {
22287
+ void client?.close().catch(() => void 0);
22288
+ };
22289
+ signal?.addEventListener("abort", onAbort, { once: true });
22263
22290
  try {
22264
22291
  const serverDevice = (await this.context.server.listDevices()).find((device) => device.deviceId === targetDeviceId);
22292
+ signal?.throwIfAborted();
22265
22293
  if (serverDevice === void 0) {
22266
22294
  throw new ClientModeError("MEMBERSHIP_REQUIRED", "The selected Host is not authorized for this account.");
22267
22295
  }
22268
22296
  this.updateConnectionProgress(progressRunId, "authorizing-peer");
22269
22297
  const target2 = await this.authorizeHostPeer(serverDevice);
22298
+ signal?.throwIfAborted();
22270
22299
  const presence = await this.context.server.presenceFor(targetDeviceId);
22300
+ signal?.throwIfAborted();
22271
22301
  if (!presence.online) throw new ClientModeError("HOST_OFFLINE", "The selected Host is offline.", true);
22272
22302
  const credentials = await this.context.server.authenticate(identity2);
22303
+ signal?.throwIfAborted();
22273
22304
  const rtcFactory = this.context.config.forceRelay ? void 0 : await this.context.rtcFactoryProvider({ routeTargets: [this.context.server.baseUrl] }).catch(() => void 0);
22274
22305
  if (!this.context.config.forceRelay && rtcFactory === void 0) {
22275
22306
  this.context.logger.warn("remote Harness WebRTC backend unavailable; using relay", {
@@ -22315,16 +22346,8 @@ var ClientConnection = class {
22315
22346
  transport = createTransport(attempt);
22316
22347
  client = new RemoteClientCore(new ClientSecureTransport(transport, identity2, target2), 6e4);
22317
22348
  const attemptClient = client;
22318
- const onAbort = () => {
22319
- void attemptClient.close();
22320
- };
22321
- signal?.addEventListener("abort", onAbort, { once: true });
22322
- try {
22323
- signal?.throwIfAborted();
22324
- await attemptClient.connect();
22325
- } finally {
22326
- signal?.removeEventListener("abort", onAbort);
22327
- }
22349
+ signal?.throwIfAborted();
22350
+ await attemptClient.connect();
22328
22351
  signal?.throwIfAborted();
22329
22352
  } finally {
22330
22353
  stopProgressTimer();
@@ -22382,6 +22405,7 @@ var ClientConnection = class {
22382
22405
  });
22383
22406
  }
22384
22407
  const features = await probeRemoteHostFeatures(connectedClient, serverDevice.clientVersion);
22408
+ signal?.throwIfAborted();
22385
22409
  return {
22386
22410
  client: connectedClient,
22387
22411
  target: target2,
@@ -22395,6 +22419,8 @@ var ClientConnection = class {
22395
22419
  this.clearConnectionProgress(progressRunId);
22396
22420
  await client?.close().catch(() => void 0);
22397
22421
  throw error;
22422
+ } finally {
22423
+ signal?.removeEventListener("abort", onAbort);
22398
22424
  }
22399
22425
  }
22400
22426
  beginAttemptProgress(runId, attempt) {
@@ -22453,10 +22479,10 @@ import { WebSocket as WebSocket2 } from "ws";
22453
22479
 
22454
22480
  // src/safe-error.ts
22455
22481
  var RpcError = class extends Error {
22456
- constructor(code, message, details, retryable = false) {
22482
+ constructor(code, message, details2, retryable = false) {
22457
22483
  super(message);
22458
22484
  this.code = code;
22459
- this.details = details;
22485
+ this.details = details2;
22460
22486
  this.retryable = retryable;
22461
22487
  }
22462
22488
  };
@@ -22985,6 +23011,7 @@ var BOT_REMOTE_OPERATIONS = [
22985
23011
  "send",
22986
23012
  "answer-question",
22987
23013
  "dismiss-question",
23014
+ "view-reply",
22988
23015
  "approval-answer",
22989
23016
  "stop-member-work"
22990
23017
  ];
@@ -23529,7 +23556,7 @@ async function loadCatalog(client, signal, pendingThreads) {
23529
23556
  return session === void 0 ? void 0 : { thread, session };
23530
23557
  }).filter((value) => value !== void 0);
23531
23558
  const projectWorkspaces = projects.map(projectWorkspace);
23532
- const workspaceByProjectId = new Map(projects.map((project, index) => [project.id, projectWorkspaces[index]]));
23559
+ const workspaceByProjectId = new Map(projects.map((project2, index) => [project2.id, projectWorkspaces[index]]));
23533
23560
  const visibleSessionIds = /* @__PURE__ */ new Set();
23534
23561
  const visibleThreads = [];
23535
23562
  for (const { thread, session } of projected) {
@@ -23558,8 +23585,8 @@ async function loadCodexProjects(client, signal) {
23558
23585
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23559
23586
  }, signal));
23560
23587
  for (const value of array2(result.data)) {
23561
- const project = projectCodexProject(value, projects.length);
23562
- if (project !== void 0 && !projects.some((item) => item.id === project.id)) projects.push(project);
23588
+ const project2 = projectCodexProject(value, projects.length);
23589
+ if (project2 !== void 0 && !projects.some((item) => item.id === project2.id)) projects.push(project2);
23563
23590
  }
23564
23591
  cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
23565
23592
  if (cursor2 === void 0) break;
@@ -23635,16 +23662,16 @@ function nativeWorkspace2(view) {
23635
23662
  function nativeVisibleWorkspaces(catalog, selectedWorkspaceId) {
23636
23663
  return catalog.workspaces.filter((workspace) => workspace.sessionIds.length > 0 || workspace.workspaceId === selectedWorkspaceId);
23637
23664
  }
23638
- function projectWorkspace(project) {
23639
- const root = project.roots[0];
23665
+ function projectWorkspace(project2) {
23666
+ const root = project2.roots[0];
23640
23667
  return {
23641
- workspaceId: codexProjectWorkspaceId(project.id),
23668
+ workspaceId: codexProjectWorkspaceId(project2.id),
23642
23669
  path: root.path,
23643
- title: project.name.trim() || basename(root.path),
23670
+ title: project2.name.trim() || basename(root.path),
23644
23671
  sessionIds: [],
23645
23672
  sessionCount: 0,
23646
- createdAt: isoTime(project.createdAt),
23647
- updatedAt: isoTime(project.updatedAt || project.createdAt)
23673
+ createdAt: isoTime(project2.createdAt),
23674
+ updatedAt: isoTime(project2.updatedAt || project2.createdAt)
23648
23675
  };
23649
23676
  }
23650
23677
  function projectCodexProject(value, fallbackPosition) {
@@ -23667,7 +23694,7 @@ function findProjectWorkspace(thread, session, projects, workspaceByProjectId) {
23667
23694
  const explicitWorkspace = explicitProjectId === void 0 ? void 0 : workspaceByProjectId.get(explicitProjectId);
23668
23695
  if (explicitWorkspace !== void 0) return explicitWorkspace;
23669
23696
  if (session.cwd === void 0) return void 0;
23670
- const match = projects.flatMap((project) => project.roots.map((root) => ({ project, root }))).filter((value) => containsPath(value.root.path, session.cwd)).sort((left, right) => {
23697
+ const match = projects.flatMap((project2) => project2.roots.map((root) => ({ project: project2, root }))).filter((value) => containsPath(value.root.path, session.cwd)).sort((left, right) => {
23671
23698
  const length = right.root.path.length - left.root.path.length;
23672
23699
  if (length !== 0) return length;
23673
23700
  const position = left.project.position - right.project.position;
@@ -24069,8 +24096,8 @@ function rcEndpoint2(endpoint) {
24069
24096
  function success2(value) {
24070
24097
  return { ok: true, value };
24071
24098
  }
24072
- function failure2(code, message, details = {}) {
24073
- return { ok: false, error: { code, message, details } };
24099
+ function failure2(code, message, details2 = {}) {
24100
+ return { ok: false, error: { code, message, details: details2 } };
24074
24101
  }
24075
24102
  function ok2(value) {
24076
24103
  return value === void 0 ? { ok: true } : { ok: true, value };
@@ -24084,8 +24111,8 @@ function business2(value) {
24084
24111
  }
24085
24112
  return ok2(value);
24086
24113
  }
24087
- function fail3(code, message, details = {}) {
24088
- return { ok: false, error: { code, message, details } };
24114
+ function fail3(code, message, details2 = {}) {
24115
+ return { ok: false, error: { code, message, details: details2 } };
24089
24116
  }
24090
24117
  function failFrom2(error) {
24091
24118
  const source = error instanceof Error ? error : new Error(String(error));
@@ -24203,12 +24230,12 @@ var CodexEventCarrier = class {
24203
24230
  } else if (event === "api-session/removed" && sessionId !== void 0) {
24204
24231
  this.broadcastRcHost({ type: "host/session-removed", sessionId });
24205
24232
  } else if (event === "api-session/added" && isRecord5(args[0])) {
24206
- const summary = args[0];
24233
+ const summary2 = args[0];
24207
24234
  this.broadcastRcHost({
24208
24235
  type: "host/session-added",
24209
- sessionId: summary.sessionId,
24210
- blank: summary.blank === true,
24211
- ...typeof summary.cwd === "string" ? { cwd: summary.cwd } : {}
24236
+ sessionId: summary2.sessionId,
24237
+ blank: summary2.blank === true,
24238
+ ...typeof summary2.cwd === "string" ? { cwd: summary2.cwd } : {}
24212
24239
  });
24213
24240
  }
24214
24241
  }
@@ -27404,7 +27431,7 @@ function normalizeServerUrl(value) {
27404
27431
  }
27405
27432
 
27406
27433
  // src/version.ts
27407
- var PLUGIN_VERSION = "0.5.13";
27434
+ var PLUGIN_VERSION = "0.5.15";
27408
27435
 
27409
27436
  // src/server-api.ts
27410
27437
  var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
@@ -28278,8 +28305,8 @@ var TypertGatewaySwitch = class {
28278
28305
  if (normalized !== void 0) return normalized;
28279
28306
  const source = error instanceof Error ? error : new Error("The Harness Gateway rejected the request.");
28280
28307
  const code = "code" in source && typeof source.code === "string" ? source.code : "internal";
28281
- const details = "details" in source && isRecord11(source.details) ? source.details : {};
28282
- return { code, message: source.message, details };
28308
+ const details2 = "details" in source && isRecord11(source.details) ? source.details : {};
28309
+ return { code, message: source.message, details: details2 };
28283
28310
  }
28284
28311
  };
28285
28312
  function workspaceConnectionResult(endpoint, result) {
@@ -29530,8 +29557,16 @@ var ClientModeRuntime = class {
29530
29557
  this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
29531
29558
  this.gatewaySwitch = new TypertGatewaySwitch(typertGateway);
29532
29559
  }
29533
- connect(targetDeviceId, signal) {
29534
- return this.connection.connect(targetDeviceId, signal);
29560
+ async connect(targetDeviceId, signal) {
29561
+ const lifetime = this.remoteLifetime.signal;
29562
+ const connectionSignal = signal === void 0 ? lifetime : AbortSignal.any([lifetime, signal]);
29563
+ connectionSignal.throwIfAborted();
29564
+ const remote = await this.connection.connect(targetDeviceId, connectionSignal);
29565
+ if (connectionSignal.aborted || this.closed) {
29566
+ await remote.client.close().catch(() => void 0);
29567
+ throw new ClientModeError("CANCELLED", "The Remote connection request was superseded or closed.");
29568
+ }
29569
+ return remote;
29535
29570
  }
29536
29571
  handleControl(endpoint, payload, signal) {
29537
29572
  return this.control.handleControl(endpoint, payload, signal);
@@ -29555,6 +29590,7 @@ var ClientModeRuntime = class {
29555
29590
  connectionProgress;
29556
29591
  connectionProgressRun = 0;
29557
29592
  closed = false;
29593
+ remoteLifetime = new AbortController();
29558
29594
  async start() {
29559
29595
  if (this.closed) throw new Error("client remote-mode runtime is closed");
29560
29596
  this.identity = await this.identities.loadOrCreate(this.config.deviceName);
@@ -29625,12 +29661,12 @@ var ClientModeRuntime = class {
29625
29661
  async detailedStatus() {
29626
29662
  const connected = this.connected;
29627
29663
  if (connected === void 0 || this.identity === void 0) return this.status();
29628
- const details = await connected.transport.connectionDetails();
29664
+ const details2 = await connected.transport.connectionDetails();
29629
29665
  if (this.connected !== connected) return this.status();
29630
29666
  return {
29631
29667
  ...this.status(),
29632
29668
  network: {
29633
- ...details,
29669
+ ...details2,
29634
29670
  local: {
29635
29671
  deviceId: this.identity.deviceId,
29636
29672
  name: this.identity.name,
@@ -29698,21 +29734,13 @@ var ClientModeRuntime = class {
29698
29734
  return result;
29699
29735
  }
29700
29736
  async clearClientAuthorization() {
29701
- const previous = this.connected;
29702
- this.connected = void 0;
29703
- this.connectionProgress = void 0;
29704
- this.pendingWorkspaceSelection = void 0;
29705
- await this.closeCodexVirtual();
29737
+ const cleanup = this.leaveRemote();
29706
29738
  this.codexOpenedSessions.clear();
29707
- await this.closeCursorVirtual();
29708
- this.proxySwitch?.selectLocal();
29709
- await this.closePreview();
29710
- this.gatewaySwitch.selectLocal();
29711
- await this.codexStreamBridge.closeCodexStreams(previous?.client);
29712
- await previous?.client.close().catch(() => void 0);
29713
- await this.server.revokeCurrentDevice();
29739
+ const results = await Promise.allSettled([cleanup, this.server.revokeCurrentDevice()]);
29714
29740
  this.identity = await this.identities.reset(this.config.deviceName);
29715
29741
  this.server.bindIdentity(this.identity);
29742
+ const failure4 = results.find((result) => result.status === "rejected");
29743
+ if (failure4?.status === "rejected") throw failure4.reason;
29716
29744
  }
29717
29745
  async setHostAuthorization(enabled) {
29718
29746
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
@@ -29726,23 +29754,14 @@ var ClientModeRuntime = class {
29726
29754
  }
29727
29755
  async setMode(mode2, targetDeviceId, signal) {
29728
29756
  if (mode2 === "local") {
29729
- await this.closeCodexVirtual();
29730
- await this.closeCursorVirtual();
29731
- this.proxySwitch?.selectLocal();
29732
- await this.closePreview();
29733
- this.gatewaySwitch.selectLocal();
29734
- const previous2 = this.connected;
29735
- this.connected = void 0;
29736
- this.connectionProgress = void 0;
29737
- this.pendingWorkspaceSelection = void 0;
29738
- await this.codexStreamBridge.closeCodexStreams(previous2?.client);
29739
- await previous2?.client.close().catch(() => void 0);
29757
+ await this.leaveRemote();
29740
29758
  this.logger.info("Harness target switched", { mode: "local" });
29741
29759
  return this.status();
29742
29760
  }
29743
29761
  if (targetDeviceId === void 0 || targetDeviceId.length === 0) {
29744
29762
  throw new ClientModeError("INVALID_MESSAGE", "A targetDeviceId is required for remote mode.");
29745
29763
  }
29764
+ const lifetime = this.remoteLifetime.signal;
29746
29765
  const next = await this.connect(targetDeviceId, signal);
29747
29766
  try {
29748
29767
  this.assertRemoteCompatible(next);
@@ -29753,11 +29772,17 @@ var ClientModeRuntime = class {
29753
29772
  }
29754
29773
  const previous = this.connected;
29755
29774
  await this.closePreview();
29775
+ if (lifetime.aborted || this.closed) {
29776
+ await next.client.close().catch(() => void 0);
29777
+ throw new ClientModeError("CANCELLED", "The Remote connection request was superseded or closed.");
29778
+ }
29756
29779
  this.connected = next;
29757
29780
  this.connection.clearConnectionProgress(next.progressRunId);
29758
29781
  this.pendingWorkspaceSelection = void 0;
29759
29782
  await this.closeCodexVirtual();
29783
+ this.assertRemoteCurrent(next, signal);
29760
29784
  await this.closeCursorVirtual();
29785
+ this.assertRemoteCurrent(next, signal);
29761
29786
  this.selectRemoteTarget(next);
29762
29787
  await this.codexStreamBridge.closeCodexStreams(previous?.client);
29763
29788
  await previous?.client.close().catch(() => void 0);
@@ -29815,8 +29840,11 @@ var ClientModeRuntime = class {
29815
29840
  });
29816
29841
  workspace = unwrapNativeResult(response);
29817
29842
  }
29843
+ this.assertRemoteCurrent(remote, signal);
29818
29844
  await this.closeCodexVirtual();
29845
+ this.assertRemoteCurrent(remote, signal);
29819
29846
  await this.closeCursorVirtual();
29847
+ this.assertRemoteCurrent(remote, signal);
29820
29848
  this.selectRemoteTarget(remote, transport);
29821
29849
  const workspaceId = workspaceRecordId(workspace.workspace);
29822
29850
  this.pendingWorkspaceSelection = { targetDeviceId: remote.target.deviceId, workspaceId };
@@ -29897,11 +29925,12 @@ var ClientModeRuntime = class {
29897
29925
  roots: [{ path: trimmedPath }],
29898
29926
  idempotencyKey: uuidV7()
29899
29927
  }, signal));
29900
- const project = record2(result.project);
29901
- if (typeof project.id !== "string" || project.id.length === 0) {
29928
+ const project2 = record2(result.project);
29929
+ this.assertRemoteCurrent(remote, signal);
29930
+ if (typeof project2.id !== "string" || project2.id.length === 0) {
29902
29931
  throw new ClientModeError("INVALID_MESSAGE", "The Host returned an invalid CodeX project.");
29903
29932
  }
29904
- return this.openCodexWorkspace(targetDeviceId, codexProjectWorkspaceId(project.id), signal);
29933
+ return this.openCodexWorkspace(targetDeviceId, codexProjectWorkspaceId(project2.id), signal);
29905
29934
  }
29906
29935
  async listCursorWorkspaces(targetDeviceId, signal, backend = "cursor") {
29907
29936
  const remote = await this.ensureConnected(targetDeviceId, signal);
@@ -29929,23 +29958,32 @@ var ClientModeRuntime = class {
29929
29958
  }, backend, remote.features.acpModelLoadsSession ? ["agent.acp.antigravity.model-load.v1"] : [], new RemoteTypertGateway2(remote.client));
29930
29959
  let workspace;
29931
29960
  try {
29932
- workspace = await virtual.selectWorkspace(workspaceId);
29933
- } catch {
29961
+ try {
29962
+ workspace = await virtual.selectWorkspace(workspaceId);
29963
+ } catch {
29964
+ this.assertRemoteCurrent(remote, signal);
29965
+ throw new ClientModeError("WORKSPACE_NOT_FOUND", `The selected ${backend} workspace is no longer available.`);
29966
+ }
29967
+ this.assertRemoteCurrent(remote, signal);
29968
+ await this.closeCodexVirtual();
29969
+ this.assertRemoteCurrent(remote, signal);
29970
+ await this.closeCursorVirtual();
29971
+ this.assertRemoteCurrent(remote, signal);
29972
+ const preferredSessionId = await virtual.preferredSessionId();
29973
+ this.assertRemoteCurrent(remote, signal);
29974
+ this.cursorVirtual = virtual;
29975
+ this.acpVirtualBackend = backend;
29976
+ this.selectCursorTarget(virtual, remote);
29977
+ this.pendingWorkspaceSelection = {
29978
+ targetDeviceId: remote.target.deviceId,
29979
+ workspaceId: workspace.workspaceId,
29980
+ backend,
29981
+ ...preferredSessionId === void 0 ? {} : { sessionId: preferredSessionId }
29982
+ };
29983
+ } catch (error) {
29934
29984
  await virtual.close();
29935
- throw new ClientModeError("WORKSPACE_NOT_FOUND", `The selected ${backend} workspace is no longer available.`);
29985
+ throw error;
29936
29986
  }
29937
- await this.closeCodexVirtual();
29938
- await this.closeCursorVirtual();
29939
- this.cursorVirtual = virtual;
29940
- this.acpVirtualBackend = backend;
29941
- this.selectCursorTarget(virtual, remote);
29942
- const preferredSessionId = await virtual.preferredSessionId();
29943
- this.pendingWorkspaceSelection = {
29944
- targetDeviceId: remote.target.deviceId,
29945
- workspaceId: workspace.workspaceId,
29946
- backend,
29947
- ...preferredSessionId === void 0 ? {} : { sessionId: preferredSessionId }
29948
- };
29949
29987
  this.logger.info(`${backend} virtual workspace opened`, { targetDeviceId: shortId(remote.target.deviceId) });
29950
29988
  return { ...this.status(), workspace };
29951
29989
  }
@@ -29962,19 +30000,27 @@ var ClientModeRuntime = class {
29962
30000
  deviceId: remote.target.deviceId,
29963
30001
  name: remote.target.name
29964
30002
  }, backend, remote.features.acpModelLoadsSession ? ["agent.acp.antigravity.model-load.v1"] : [], new RemoteTypertGateway2(remote.client));
29965
- const workspace = await virtual.selectOrCreateWorkspace(trimmedPath);
29966
- await this.closeCodexVirtual();
29967
- await this.closeCursorVirtual();
29968
- this.cursorVirtual = virtual;
29969
- this.acpVirtualBackend = backend;
29970
- this.selectCursorTarget(virtual, remote);
29971
- this.pendingWorkspaceSelection = {
29972
- targetDeviceId: remote.target.deviceId,
29973
- workspaceId: workspace.workspaceId,
29974
- backend
29975
- };
29976
- this.logger.info(`${backend} virtual workspace created`, { targetDeviceId: shortId(remote.target.deviceId) });
29977
- return { ...this.status(), workspace };
30003
+ try {
30004
+ const workspace = await virtual.selectOrCreateWorkspace(trimmedPath);
30005
+ this.assertRemoteCurrent(remote, signal);
30006
+ await this.closeCodexVirtual();
30007
+ this.assertRemoteCurrent(remote, signal);
30008
+ await this.closeCursorVirtual();
30009
+ this.assertRemoteCurrent(remote, signal);
30010
+ this.cursorVirtual = virtual;
30011
+ this.acpVirtualBackend = backend;
30012
+ this.selectCursorTarget(virtual, remote);
30013
+ this.pendingWorkspaceSelection = {
30014
+ targetDeviceId: remote.target.deviceId,
30015
+ workspaceId: workspace.workspaceId,
30016
+ backend
30017
+ };
30018
+ this.logger.info(`${backend} virtual workspace created`, { targetDeviceId: shortId(remote.target.deviceId) });
30019
+ return { ...this.status(), workspace };
30020
+ } catch (error) {
30021
+ await virtual.close();
30022
+ throw error;
30023
+ }
29978
30024
  }
29979
30025
  consumeWorkspaceSelection(selection) {
29980
30026
  const pending = this.pendingWorkspaceSelection;
@@ -29984,21 +30030,34 @@ var ClientModeRuntime = class {
29984
30030
  return this.status();
29985
30031
  }
29986
30032
  async close() {
29987
- await this.closePreview();
29988
30033
  if (this.closed) return;
29989
30034
  this.closed = true;
29990
- this.proxySwitch?.selectLocal();
29991
- await this.closePreview();
29992
- this.gatewaySwitch.selectLocal();
29993
- this.pendingWorkspaceSelection = void 0;
29994
- await this.closeCodexVirtual();
29995
- await this.closeCursorVirtual();
29996
- await this.codexStreamBridge.closeCodexStreams(this.connected?.client);
29997
- await this.connected?.client.close().catch(() => void 0);
30035
+ this.server.close?.();
30036
+ try {
30037
+ await this.leaveRemote();
30038
+ } finally {
30039
+ this.proxySwitch?.restore();
30040
+ this.gatewaySwitch.restore();
30041
+ }
30042
+ }
30043
+ async leaveRemote() {
30044
+ this.remoteLifetime.abort(new ClientModeError("CANCELLED", "The Remote connection was closed."));
30045
+ this.remoteLifetime = new AbortController();
30046
+ const previous = this.connected;
29998
30047
  this.connected = void 0;
29999
30048
  this.connectionProgress = void 0;
30000
- this.proxySwitch?.restore();
30001
- this.gatewaySwitch.restore();
30049
+ this.pendingWorkspaceSelection = void 0;
30050
+ this.proxySwitch?.selectLocal();
30051
+ this.gatewaySwitch.selectLocal();
30052
+ const results = await Promise.allSettled([
30053
+ this.closeCodexVirtual(),
30054
+ this.closeCursorVirtual(),
30055
+ this.closePreview(),
30056
+ this.codexStreamBridge.closeCodexStreams(previous?.client),
30057
+ previous?.client.close()
30058
+ ]);
30059
+ const failure4 = results.find((result) => result.status === "rejected");
30060
+ if (failure4?.status === "rejected") throw failure4.reason;
30002
30061
  }
30003
30062
  async callRemoteFileViewer(endpoint, payload, signal) {
30004
30063
  const remote = this.connected;
@@ -30111,11 +30170,12 @@ var ClientModeRuntime = class {
30111
30170
  );
30112
30171
  }
30113
30172
  async ensureConnected(targetDeviceId, signal, isCurrent) {
30173
+ const lifetime = this.remoteLifetime.signal;
30114
30174
  if (this.connected?.target.deviceId === targetDeviceId) return this.connected;
30115
30175
  const next = await this.connect(targetDeviceId, signal);
30116
30176
  const assertCurrent = () => {
30117
30177
  signal?.throwIfAborted();
30118
- if (this.closed || isCurrent?.() === false) {
30178
+ if (this.closed || lifetime.aborted || isCurrent?.() === false) {
30119
30179
  throw new ClientModeError("CANCELLED", "The Remote connection request was superseded or closed.");
30120
30180
  }
30121
30181
  };
@@ -30134,6 +30194,12 @@ var ClientModeRuntime = class {
30134
30194
  await previous?.client.close().catch(() => void 0);
30135
30195
  return next;
30136
30196
  }
30197
+ assertRemoteCurrent(remote, signal) {
30198
+ signal?.throwIfAborted();
30199
+ if (this.closed || this.connected !== remote) {
30200
+ throw new ClientModeError("CANCELLED", "The Remote workspace request was superseded or closed.");
30201
+ }
30202
+ }
30137
30203
  requireIdentity() {
30138
30204
  if (this.identity === void 0) throw new ClientModeError("IDENTITY_INVALID", "The client identity is not ready.");
30139
30205
  return this.identity;
@@ -30463,7 +30529,7 @@ var ControlStatusStream = class {
30463
30529
  };
30464
30530
 
30465
30531
  // src/codex/domain.ts
30466
- import { randomUUID as randomUUID2 } from "node:crypto";
30532
+ import { randomUUID as randomUUID3 } from "node:crypto";
30467
30533
  import { accessSync, constants, existsSync as existsSync2, readFileSync } from "node:fs";
30468
30534
  import { readdir, realpath, stat as stat3 } from "node:fs/promises";
30469
30535
  import { homedir as homedir2 } from "node:os";
@@ -30835,6 +30901,121 @@ function threadIdFromParams(params) {
30835
30901
  return typeof params.threadId === "string" ? params.threadId : void 0;
30836
30902
  }
30837
30903
 
30904
+ // src/compact-history.ts
30905
+ var LIMIT = 512;
30906
+ var object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
30907
+ var summary = (value) => {
30908
+ const text = typeof value === "string" ? value : JSON.stringify(value) ?? "";
30909
+ return text.length > LIMIT ? text.slice(0, LIMIT) + "\n[Compact history: output omitted]" : text;
30910
+ };
30911
+ function toolValue(value) {
30912
+ if (Array.isArray(value)) return value.map(toolValue);
30913
+ if (!object(value)) return value;
30914
+ const result = { ...value };
30915
+ for (const [key, child] of Object.entries(value)) {
30916
+ if (["input", "arguments", "output", "result", "rawInput", "rawOutput", "uiResultMeta", "meta", "argumentsDelta"].includes(key)) {
30917
+ result[key] = typeof child === "string" ? summary(child) : { summary: summary(child) };
30918
+ } else if (key === "content" && Array.isArray(child)) {
30919
+ let previews = 0;
30920
+ const blocks = child.flatMap((block) => {
30921
+ if (object(block) && (block.type === "tool-call" || block.type === "tool-result")) return [toolValue(block)];
30922
+ if (previews++ >= 2) return [];
30923
+ return [{ type: "text", text: summary(object(block) ? block.text ?? block : block) }];
30924
+ });
30925
+ if (child.some((block) => object(block) && block.isError === true) && object(blocks[0])) blocks[0].isError = true;
30926
+ result[key] = blocks;
30927
+ } else if (key === "text" && typeof child === "string") result[key] = summary(child);
30928
+ else result[key] = toolValue(child);
30929
+ }
30930
+ return result;
30931
+ }
30932
+ function messageValue(value) {
30933
+ if (Array.isArray(value)) return value.map(messageValue);
30934
+ if (!object(value)) return value;
30935
+ if (value.type === "tool-call" || value.type === "tool-result") return toolValue(value);
30936
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, messageValue(child)]));
30937
+ }
30938
+ function project(value) {
30939
+ if (Array.isArray(value)) return value.map(project);
30940
+ if (!object(value)) return value;
30941
+ if (object(value.event) && ["tool/call", "tool/result"].includes(String(value.event.type))) {
30942
+ const { view, ...rest } = value;
30943
+ const event = toolValue(value.event);
30944
+ const data2 = object(event.data) ? event.data : {};
30945
+ const title = object(view) && object(view.view) ? view.view.title : void 0;
30946
+ return { ...rest, event: { ...event, data: { ...data2, ...typeof title === "string" && !data2.name && !data2.toolName ? { toolName: title } : {} } } };
30947
+ }
30948
+ if (typeof value.type === "string" && ["tool/call", "tool/result"].includes(value.type)) return toolValue(value);
30949
+ if (typeof value.type === "string" && ["user/message", "assistant/message"].includes(value.type)) return messageValue(value);
30950
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, project(child)]));
30951
+ }
30952
+ function compactHistory(value) {
30953
+ return project(value);
30954
+ }
30955
+
30956
+ // src/codex/history-preview.ts
30957
+ import { randomUUID as randomUUID2 } from "node:crypto";
30958
+ var object2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : {};
30959
+ var details = /* @__PURE__ */ new WeakMap();
30960
+ var MAX_CACHE = 16 * 1024 * 1024;
30961
+ var size = (v) => Buffer.byteLength(JSON.stringify(v), "utf8");
30962
+ async function previewCall(peer, input3, invoke) {
30963
+ const call = object2(input3), params = object2(call.params);
30964
+ if (call.method === "dsh/historyDetail") {
30965
+ if (Object.keys(call).some((k) => !["method", "params"].includes(k)) || Object.keys(params).some((k) => !["threadId", "detailId"].includes(k)) || typeof params.threadId !== "string" || typeof params.detailId !== "string") throw new RpcError("INVALID_MESSAGE", "Invalid history detail request.");
30966
+ const detail = details.get(peer)?.get(params.detailId);
30967
+ if (!detail || detail.threadId !== params.threadId || detail.until < Date.now()) throw new RpcError("HISTORY_DETAIL_EXPIRED", "Tool details expired. Reopen the conversation and try again.");
30968
+ await invoke({ method: "thread/read", params: { threadId: detail.threadId, includeTurns: false } });
30969
+ return detail.record;
30970
+ }
30971
+ if (call.previewHistory !== true) return invoke(input3);
30972
+ if (call.method !== "dsh/sessionHistory" || Object.keys(call).some((k) => !["method", "params", "previewHistory"].includes(k)))
30973
+ throw new RpcError("INVALID_MESSAGE", "History preview is read-only.");
30974
+ const result = object2(await invoke({ method: call.method, params: call.params }));
30975
+ if (!Array.isArray(result.records) || typeof params.threadId !== "string") return result;
30976
+ let cache = details.get(peer);
30977
+ if (!cache) details.set(peer, cache = /* @__PURE__ */ new Map());
30978
+ for (const [id5, value] of cache) if (value.until < Date.now()) cache.delete(id5);
30979
+ const pending = /* @__PURE__ */ new Map();
30980
+ let pendingBytes = 0;
30981
+ const records = result.records.map((raw) => {
30982
+ const record9 = object2(raw), event = object2(record9.event), data2 = object2(event.data);
30983
+ if (event.type !== "tool/result" || size(record9) < 4096) return raw;
30984
+ const bytes = size(record9);
30985
+ if (bytes > MAX_CACHE) return raw;
30986
+ if (pending.size >= 64 || pendingBytes + bytes > MAX_CACHE) {
30987
+ throw new RpcError("RESPONSE_TOO_LARGE", "History requires chunked transfer.");
30988
+ }
30989
+ const id5 = randomUUID2();
30990
+ pending.set(id5, { threadId: params.threadId, record: raw, bytes, until: Date.now() + 10 * 6e4 });
30991
+ pendingBytes += bytes;
30992
+ const message = object2(data2.message);
30993
+ return { ...record9, view: void 0, event: { ...event, data: {
30994
+ id: data2.id,
30995
+ callId: data2.callId,
30996
+ name: data2.name,
30997
+ toolName: data2.toolName,
30998
+ turn: data2.turn,
30999
+ step: data2.step,
31000
+ message: { source: message.source, isError: message.isError === true || Array.isArray(message.content) && message.content.some((v) => object2(v).isError === true), content: [{ type: "text", text: "Expand to load tool output" }] },
31001
+ isError: data2.isError === true,
31002
+ failed: data2.failed,
31003
+ error: data2.error ? { message: "Tool execution failed" } : void 0,
31004
+ remoteDetail: { threadId: params.threadId, detailId: id5 }
31005
+ } } };
31006
+ });
31007
+ const preview = { ...result, records };
31008
+ if (size(preview) > 2 * 1024 * 1024) throw new RpcError("RESPONSE_TOO_LARGE", "History requires chunked transfer.");
31009
+ let cachedBytes = [...cache.values()].reduce((n, value) => n + value.bytes, 0);
31010
+ while (cache.size + pending.size > 64 || cachedBytes + pendingBytes > MAX_CACHE) {
31011
+ const oldest = cache.keys().next().value;
31012
+ cachedBytes -= cache.get(oldest).bytes;
31013
+ cache.delete(oldest);
31014
+ }
31015
+ for (const [id5, detail] of pending) cache.set(id5, detail);
31016
+ return preview;
31017
+ }
31018
+
30838
31019
  // src/codex/peer-bridge.ts
30839
31020
  import { Buffer as Buffer3 } from "node:buffer";
30840
31021
  var streamOpenSchema = external_exports.object({
@@ -30864,6 +31045,7 @@ var CodexPeerBridge = class {
30864
31045
  this.publish = publish;
30865
31046
  this.logger = logger;
30866
31047
  }
31048
+ compactHistoryEnabled = false;
30867
31049
  streams = /* @__PURE__ */ new Map();
30868
31050
  incomingTransfers = /* @__PURE__ */ new Map();
30869
31051
  outgoingTransfers = /* @__PURE__ */ new Map();
@@ -30874,7 +31056,8 @@ var CodexPeerBridge = class {
30874
31056
  async callDomain(input3, logFailure) {
30875
31057
  this.requireOpen();
30876
31058
  try {
30877
- return await this.domain.call(this.context.connectionId, input3);
31059
+ const result = await previewCall(this, input3, (value) => this.domain.call(this.context.connectionId, value));
31060
+ return this.compactHistoryEnabled && safeMethod(input3) === "dsh/sessionHistory" ? compactHistory(result) : result;
30878
31061
  } catch (error) {
30879
31062
  if (logFailure) {
30880
31063
  this.logger?.warn("Codex call failed", {
@@ -31640,7 +31823,7 @@ var CodexRemoteDomain = class {
31640
31823
  await appServer.respond(message.id, { decision: "decline" });
31641
31824
  return;
31642
31825
  }
31643
- const requestHandle = randomUUID2();
31826
+ const requestHandle = randomUUID3();
31644
31827
  this.approvals.set(requestHandle, {
31645
31828
  upstreamId: message.id,
31646
31829
  connectionId: owner.connectionId,
@@ -31893,10 +32076,10 @@ var CodexRemoteDomain = class {
31893
32076
  limit: CODEX_PAGE_LIMIT2,
31894
32077
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
31895
32078
  }));
31896
- for (const project of result.data) {
31897
- const projectRoots = project.roots.map((root) => root.path).filter((path) => isAbsolute2(path));
32079
+ for (const project2 of result.data) {
32080
+ const projectRoots = project2.roots.map((root) => root.path).filter((path) => isAbsolute2(path));
31898
32081
  if (projectRoots.length === 0) continue;
31899
- projectIds.add(project.id);
32082
+ projectIds.add(project2.id);
31900
32083
  roots.push(...projectRoots);
31901
32084
  }
31902
32085
  cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
@@ -32194,11 +32377,11 @@ function sanitizeProjectList(result) {
32194
32377
  };
32195
32378
  }
32196
32379
  function sanitizeProjectCreate(result) {
32197
- const project = isRecord14(result) ? sanitizeProject(result.project) : void 0;
32198
- if (project === void 0) {
32380
+ const project2 = isRecord14(result) ? sanitizeProject(result.project) : void 0;
32381
+ if (project2 === void 0) {
32199
32382
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid created project.");
32200
32383
  }
32201
- return { project };
32384
+ return { project: project2 };
32202
32385
  }
32203
32386
  function isThreadAllowedByWorkspaceAuthority(thread, authority) {
32204
32387
  const projectId = typeof thread.projectId === "string" ? thread.projectId : void 0;
@@ -34087,7 +34270,7 @@ var AcpWorkspaceTools = class {
34087
34270
  };
34088
34271
 
34089
34272
  // src/acp/gateway.ts
34090
- import { randomUUID as randomUUID4 } from "node:crypto";
34273
+ import { randomUUID as randomUUID5 } from "node:crypto";
34091
34274
  import { readdir as readdir3, realpath as realpath5, stat as stat5 } from "node:fs/promises";
34092
34275
  import { homedir as homedir5 } from "node:os";
34093
34276
  import { basename as basename4, isAbsolute as isAbsolute6, join as join11, relative as relative4, resolve as resolve4 } from "node:path";
@@ -34403,7 +34586,7 @@ function agySelectionArgs(catalog, modelId, effort) {
34403
34586
  import { promises as fs, realpathSync as realpathSync2, constants as constants3 } from "node:fs";
34404
34587
  import { tmpdir } from "node:os";
34405
34588
  import { join as join8, basename as basename3 } from "node:path";
34406
- import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
34589
+ import { createHash as createHash2, randomUUID as randomUUID4 } from "node:crypto";
34407
34590
  var EXTENSIONS = { "image/png": "png", "image/jpeg": "jpg", "image/webp": "webp", "image/gif": "gif" };
34408
34591
  var IMAGE_NAME = /^[0-9a-f-]{36}\.(png|jpg|webp|gif)$/;
34409
34592
  var TTL = 24 * 60 * 60 * 1e3;
@@ -34448,7 +34631,7 @@ async function stageImages(sessionId, images, root) {
34448
34631
  const paths = [];
34449
34632
  try {
34450
34633
  for (const image of parsed) {
34451
- const path = join8(directory, `${randomUUID3()}.${EXTENSIONS[image.mimeType]}`);
34634
+ const path = join8(directory, `${randomUUID4()}.${EXTENSIONS[image.mimeType]}`);
34452
34635
  await fs.writeFile(path, Buffer.from(image.data, "base64"), { flag: "wx", mode: 384 });
34453
34636
  paths.push(path);
34454
34637
  }
@@ -36238,6 +36421,7 @@ var AcpPeerBridge = class {
36238
36421
  this.publish = publish;
36239
36422
  this.logger = logger;
36240
36423
  }
36424
+ compactHistoryEnabled = false;
36241
36425
  toolControllers = /* @__PURE__ */ new Map();
36242
36426
  streams = /* @__PURE__ */ new Map();
36243
36427
  incomingTransfers = /* @__PURE__ */ new Map();
@@ -36252,7 +36436,8 @@ var AcpPeerBridge = class {
36252
36436
  async callDomain(input3, logFailure) {
36253
36437
  this.requireOpen();
36254
36438
  try {
36255
- return await this.domain.call(this.context.connectionId, input3);
36439
+ const result = await this.domain.call(this.context.connectionId, input3);
36440
+ return this.compactHistoryEnabled && safeMethod2(input3) === "dsh/sessionHistory" ? compactHistory(result) : result;
36256
36441
  } catch (error) {
36257
36442
  if (logFailure) {
36258
36443
  this.logger?.warn("Cursor call failed", {
@@ -37358,7 +37543,7 @@ var AcpRemoteGateway = class {
37358
37543
  await client.respondError(message.id, -32602, "The ACP permission options are invalid.");
37359
37544
  return;
37360
37545
  }
37361
- const requestHandle = randomUUID4();
37546
+ const requestHandle = randomUUID5();
37362
37547
  this.approvals.set(requestHandle, {
37363
37548
  upstreamId: message.id,
37364
37549
  connectionId: owner,
@@ -37765,13 +37950,16 @@ function isRecord19(value) {
37765
37950
 
37766
37951
  // src/control-runtime.ts
37767
37952
  var PluginControlRuntime = class {
37768
- constructor(config, identityDirectory, settings, client, host) {
37953
+ constructor(config, identityDirectory, settings, client, host, switchServer) {
37769
37954
  this.config = config;
37770
37955
  this.identityDirectory = identityDirectory;
37771
37956
  this.settings = settings;
37772
37957
  this.client = client;
37773
37958
  this.host = host;
37959
+ this.switchServer = switchServer;
37774
37960
  }
37961
+ logoutFlight;
37962
+ serverSwitchFlight;
37775
37963
  register(connection, webServer) {
37776
37964
  const statusStream = new ControlStatusStream(() => this.streamStatus());
37777
37965
  return registerControlRoute(
@@ -37793,6 +37981,7 @@ var PluginControlRuntime = class {
37793
37981
  }
37794
37982
  async handle(endpoint, payload, signal) {
37795
37983
  try {
37984
+ if (endpoint !== "settings.server.set") await this.serverSwitchFlight;
37796
37985
  if (endpoint === "settings.development.set") return ok3(await this.setDevelopment(payload));
37797
37986
  if (endpoint === "settings.get") return ok3(await this.settingsView());
37798
37987
  if (endpoint === "settings.configure") return ok3(await this.configure(payload));
@@ -37886,6 +38075,19 @@ var PluginControlRuntime = class {
37886
38075
  };
37887
38076
  }
37888
38077
  async setServer(payload) {
38078
+ if (this.serverSwitchFlight !== void 0) {
38079
+ throw new ClientModeError("SERVER_SWITCH_BUSY", "A Server switch is already in progress.");
38080
+ }
38081
+ const flight = this.applyServer(payload);
38082
+ this.serverSwitchFlight = flight;
38083
+ try {
38084
+ return await flight;
38085
+ } finally {
38086
+ if (this.serverSwitchFlight === flight) this.serverSwitchFlight = void 0;
38087
+ }
38088
+ }
38089
+ async applyServer(payload) {
38090
+ await this.logoutFlight;
37889
38091
  if (this.settings === void 0) {
37890
38092
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
37891
38093
  }
@@ -37896,6 +38098,15 @@ var PluginControlRuntime = class {
37896
38098
  const current = editableConfig(resolveConfig(this.settings.get()));
37897
38099
  const next = resolveConfig({ ...current, serverUrl: value.serverUrl });
37898
38100
  await this.settings.replace(editableConfig(next));
38101
+ if (this.switchServer !== void 0) {
38102
+ if (next.serverUrl !== this.config.serverUrl) {
38103
+ const runtimes = await this.switchServer(next);
38104
+ this.client = runtimes.client;
38105
+ this.host = runtimes.host;
38106
+ }
38107
+ this.config = next;
38108
+ return { ...await this.settingsView(), applies: "live" };
38109
+ }
37899
38110
  return this.settingsView();
37900
38111
  }
37901
38112
  async setRole(payload) {
@@ -38017,20 +38228,28 @@ var PluginControlRuntime = class {
38017
38228
  const targetApi = targetRole === "host" ? new HostServerApi(serverUrl, new ServerCredentialStore(targetDirectory)) : new ClientServerApi(serverUrl, new ServerCredentialStore(targetDirectory));
38018
38229
  await targetApi.authorizeOwnedRole(targetIdentity, sourceCredentials.accessToken, sourceCredentials.account);
38019
38230
  }
38020
- async logout() {
38021
- if (this.settings === void 0) {
38022
- throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
38023
- }
38024
- const config = resolveConfig(this.settings.get());
38231
+ logout() {
38232
+ if (this.logoutFlight !== void 0) return this.logoutFlight;
38233
+ const task = this.clearAuthorization();
38234
+ this.logoutFlight = task;
38235
+ void task.finally(() => {
38236
+ if (this.logoutFlight === task) this.logoutFlight = void 0;
38237
+ }).catch(() => void 0);
38238
+ return task;
38239
+ }
38240
+ async clearAuthorization() {
38241
+ const config = this.settings === void 0 ? this.config : resolveConfig(this.settings.get());
38025
38242
  if (config.serverUrl !== void 0) {
38026
- await Promise.all([
38243
+ const runtimes = await Promise.allSettled([
38027
38244
  this.client?.clearClientAuthorization(),
38028
38245
  this.host?.clearHostAuthorization()
38029
38246
  ]);
38030
- await Promise.all(["host", "client"].map(async (role) => {
38247
+ const stores = await Promise.allSettled(["host", "client"].map(async (role) => {
38031
38248
  const directory = serverStorageDirectory(this.identityDirectory, config.serverUrl, role);
38032
38249
  await new ServerCredentialStore(directory).clear();
38033
38250
  }));
38251
+ const failure4 = [...runtimes, ...stores].find((result) => result.status === "rejected");
38252
+ if (failure4?.status === "rejected") throw failure4.reason;
38034
38253
  }
38035
38254
  return this.settingsView();
38036
38255
  }
@@ -38177,7 +38396,7 @@ function redact(value, key = "") {
38177
38396
  }
38178
38397
 
38179
38398
  // src/workspace-upload.ts
38180
- import { randomUUID as randomUUID5 } from "node:crypto";
38399
+ import { randomUUID as randomUUID6 } from "node:crypto";
38181
38400
  import { constants as constants4 } from "node:fs";
38182
38401
  import { link, lstat as lstat2, open as open2, realpath as realpath6, stat as stat6, unlink } from "node:fs/promises";
38183
38402
  import { extname, isAbsolute as isAbsolute7, join as join12 } from "node:path";
@@ -38257,7 +38476,7 @@ var WorkspaceUploadHost = class {
38257
38476
  const root = await realpath6(sourceRoot);
38258
38477
  const directory = await stat6(root);
38259
38478
  if (!directory.isDirectory()) throw failure3("UPLOAD_WORKSPACE_UNAVAILABLE");
38260
- const temp = join12(root, `.dsh-upload-${randomUUID5()}.part`);
38479
+ const temp = join12(root, `.dsh-upload-${randomUUID6()}.part`);
38261
38480
  const handle = await open2(temp, constants4.O_CREAT | constants4.O_EXCL | constants4.O_RDWR | (constants4.O_NOFOLLOW ?? 0), 384);
38262
38481
  const upload = { ...p, received: 0, touched: Date.now(), root, sourceRoot, dev: directory.dev, ino: directory.ino, temp, handle };
38263
38482
  this.uploads.set(p.uploadId, upload);
@@ -38392,7 +38611,7 @@ function unavailable2() {
38392
38611
  }
38393
38612
 
38394
38613
  // src/service.ts
38395
- import { randomUUID as randomUUID6 } from "node:crypto";
38614
+ import { randomUUID as randomUUID7 } from "node:crypto";
38396
38615
 
38397
38616
  // src/connection-controller.ts
38398
38617
  var ConnectionController = class {
@@ -38494,10 +38713,10 @@ var ConnectionController = class {
38494
38713
  const revoked = [...this.active.values()].filter((connection) => connection.channel.peerDeviceId === deviceId);
38495
38714
  await Promise.all(revoked.map((connection) => this.disconnect(connection, "DEVICE_REVOKED")));
38496
38715
  }
38497
- async closeConnection(connectionId, code) {
38716
+ async closeConnection(connectionId, code, detail) {
38498
38717
  const connection = this.active.get(connectionId);
38499
38718
  if (connection === void 0) return false;
38500
- await this.disconnect(connection, code);
38719
+ await this.disconnect(connection, code, detail);
38501
38720
  return true;
38502
38721
  }
38503
38722
  async close() {
@@ -38548,7 +38767,7 @@ var ConnectionController = class {
38548
38767
  throw error instanceof Error ? error : new Error("peer send failed", { cause: error });
38549
38768
  }
38550
38769
  }
38551
- async disconnect(connection, code) {
38770
+ async disconnect(connection, code, detail) {
38552
38771
  if (!this.isActive(connection)) return;
38553
38772
  this.active.delete(connection.channel.security.connectionId);
38554
38773
  connection.unsubscribe();
@@ -38560,7 +38779,9 @@ var ConnectionController = class {
38560
38779
  this.logger?.info("peer connection disconnected", {
38561
38780
  peerDeviceId: shortId2(connection.channel.peerDeviceId),
38562
38781
  connectionId: shortId2(connection.channel.security.connectionId),
38563
- code: code ?? "closed"
38782
+ code: code ?? "closed",
38783
+ closedBy: detail?.closedBy ?? "host",
38784
+ ...detail?.retryable === void 0 ? {} : { retryable: detail.retryable }
38564
38785
  });
38565
38786
  }
38566
38787
  isActive(connection) {
@@ -38584,8 +38805,10 @@ function shortId2(value) {
38584
38805
  // src/rpc-router.ts
38585
38806
  var wireRequestSchema = external_exports.object({ method: external_exports.string().min(1), params: external_exports.unknown() }).strict();
38586
38807
  var emptyParamsSchema = external_exports.object({}).strict();
38808
+ var historyConfigureSchema = external_exports.object({ compact: external_exports.boolean() }).strict();
38587
38809
  var apiMethods = /* @__PURE__ */ new Set([
38588
38810
  "harness.transport.describe",
38811
+ "remote.history.configure",
38589
38812
  "harness.api.call",
38590
38813
  "harness.api.transfer.open",
38591
38814
  "harness.api.transfer.chunk",
@@ -38710,7 +38933,7 @@ var RpcRouter = class {
38710
38933
  switch (method) {
38711
38934
  case "harness.transport.describe": {
38712
38935
  emptyParamsSchema.parse(params);
38713
- const capabilities = [...this.capabilities()];
38936
+ const capabilities = [.../* @__PURE__ */ new Set([...this.capabilities(), "remote.history.compact.v1"])];
38714
38937
  let workspaceTypes;
38715
38938
  if (this.workspaceTypes !== void 0) {
38716
38939
  try {
@@ -38724,6 +38947,13 @@ var RpcRouter = class {
38724
38947
  ...workspaceTypes === void 0 ? {} : { workspaceTypes }
38725
38948
  };
38726
38949
  }
38950
+ case "remote.history.configure": {
38951
+ const { compact } = historyConfigureSchema.parse(params);
38952
+ for (const bridge of [this.harnessApi, this.harnessRemote, this.codex, this.acp]) {
38953
+ if (bridge !== void 0) bridge.compactHistoryEnabled = compact;
38954
+ }
38955
+ return { compact };
38956
+ }
38727
38957
  case "harness.api.call":
38728
38958
  return this.requireApiProxy().call(params);
38729
38959
  case "harness.api.transfer.open":
@@ -39262,13 +39492,16 @@ var HostServerConnection = class {
39262
39492
  this.terminalError = payload.code;
39263
39493
  this.socket?.close(4004, "device revoked");
39264
39494
  } else if (payload.connectionId !== void 0) {
39265
- await this.dropTunnel(payload.connectionId, payload.code);
39495
+ await this.dropTunnel(payload.connectionId, payload.code, {
39496
+ closedBy: "server",
39497
+ retryable: payload.retryable
39498
+ });
39266
39499
  const fields = {
39267
39500
  code: payload.code,
39268
39501
  connectionId: shortId3(payload.connectionId),
39269
39502
  retryable: payload.retryable
39270
39503
  };
39271
- if (payload.retryable) this.logger.debug("server closed a remote connection", fields);
39504
+ if (payload.retryable) this.logger.info("server closed a remote connection", fields);
39272
39505
  else this.logger.warn("server closed a remote connection", fields);
39273
39506
  } else {
39274
39507
  this.terminalError = payload.code;
@@ -39642,7 +39875,7 @@ var HostServerConnection = class {
39642
39875
  }));
39643
39876
  await this.connections.close();
39644
39877
  }
39645
- async dropTunnel(connectionId, code) {
39878
+ async dropTunnel(connectionId, code, detail) {
39646
39879
  const tunnel = this.tunnels.get(connectionId);
39647
39880
  if (tunnel === void 0) return;
39648
39881
  this.tunnels.delete(connectionId);
@@ -39656,7 +39889,7 @@ var HostServerConnection = class {
39656
39889
  }
39657
39890
  if (tunnel.channel !== void 0) {
39658
39891
  try {
39659
- const closed = await this.connections.closeConnection(connectionId, code);
39892
+ const closed = detail === void 0 ? await this.connections.closeConnection(connectionId, code) : await this.connections.closeConnection(connectionId, code, detail);
39660
39893
  if (!closed) await tunnel.channel.close(code);
39661
39894
  } catch (error) {
39662
39895
  await tunnel.channel.close(code).catch(() => void 0);
@@ -39896,10 +40129,16 @@ function closeCode(code) {
39896
40129
  }
39897
40130
 
39898
40131
  // src/remote-directory-browser.ts
39899
- import { readdir as readdir4, stat as stat7 } from "node:fs/promises";
40132
+ import { mkdir as mkdir3, readdir as readdir4, stat as stat7 } from "node:fs/promises";
39900
40133
  import { homedir as homedir6, platform as platform2 } from "node:os";
39901
40134
  import { basename as basename5, dirname as dirname5, isAbsolute as isAbsolute8, parse, resolve as resolve5 } from "node:path";
39902
40135
  var MAX_ENTRIES = 500;
40136
+ async function ensureHostWorkspaceDirectory(path) {
40137
+ if (typeof path !== "string" || path.trim() === "" || path.includes("\0") || !isAbsolute8(path) || resolve5(path) === parse(path).root) {
40138
+ throw new RpcError("INVALID_MESSAGE", "Workspace path must be an absolute, non-root directory path.");
40139
+ }
40140
+ await mkdir3(path, { recursive: true });
40141
+ }
39903
40142
  async function listRemoteDirectory(path, signal) {
39904
40143
  signal?.throwIfAborted();
39905
40144
  const home = resolve5(homedir6());
@@ -40175,6 +40414,7 @@ var HarnessApiBridge = class {
40175
40414
  this.host = api.events.host.bind(api.events);
40176
40415
  this.answer = api.respond.bind(api);
40177
40416
  }
40417
+ compactHistoryEnabled = false;
40178
40418
  methods;
40179
40419
  streams = /* @__PURE__ */ new Map();
40180
40420
  respondable = /* @__PURE__ */ new Map();
@@ -40206,7 +40446,10 @@ var HarnessApiBridge = class {
40206
40446
  );
40207
40447
  let response;
40208
40448
  if (params.method === "session.history") {
40209
- response = await callSessionHistory(callWithTimeout, params.payload, params.rpcId);
40449
+ response = await callSessionHistory(async (payload) => {
40450
+ const result = await callWithTimeout(payload);
40451
+ return this.compactHistoryEnabled ? { ...result, result: compactHistory(result.result) } : result;
40452
+ }, params.payload, params.rpcId);
40210
40453
  } else {
40211
40454
  response = await callWithTimeout(request.payload);
40212
40455
  }
@@ -40220,7 +40463,7 @@ var HarnessApiBridge = class {
40220
40463
  method: params.method,
40221
40464
  durationMs: Math.round(performance.now() - startedAt)
40222
40465
  });
40223
- return response;
40466
+ return this.compactHistoryEnabled && params.method === "subagent.history" ? { ...response, result: compactHistory(response.result) } : response;
40224
40467
  } catch (error) {
40225
40468
  const durationMs = Math.round(performance.now() - startedAt);
40226
40469
  this.logger?.warn("harness api call failed", {
@@ -40838,9 +41081,16 @@ var HARNESS_REMOTE_ALLOWLIST = [
40838
41081
  "messageFeedback/list",
40839
41082
  "messageFeedback/put",
40840
41083
  "pluginInventory/list",
41084
+ "pluginManager/listBundles",
41085
+ "pluginManager/listPlugins",
41086
+ "pluginManager/setBundleEnabled",
41087
+ "pluginManager/setPluginEnabled",
40841
41088
  "permissionPresets/catalog",
40842
41089
  "schedule/list",
40843
41090
  "schedule/catalog",
41091
+ "schedule/history",
41092
+ "schedule/update",
41093
+ "schedule/delete",
40844
41094
  "session/attachment",
40845
41095
  "session/cancel",
40846
41096
  "session/canOpenWorkspacePath",
@@ -40905,6 +41155,7 @@ var HarnessRemoteBridge = class {
40905
41155
  this.codexWorkspace = codexWorkspace;
40906
41156
  this.terminalCarrier = usesDuplexTerminal(harnessVersion) ? new TerminalCarrierCompat(gateway) : void 0;
40907
41157
  }
41158
+ compactHistoryEnabled = false;
40908
41159
  streams = /* @__PURE__ */ new Map();
40909
41160
  incomingTransfers = /* @__PURE__ */ new Map();
40910
41161
  outgoingTransfers = /* @__PURE__ */ new Map();
@@ -40919,6 +41170,7 @@ var HarnessRemoteBridge = class {
40919
41170
  throw new RpcError("PERMISSION_DENIED", "Remote access settings can only be changed locally on the Host.");
40920
41171
  }
40921
41172
  }
41173
+ await this.checkPluginManagement(params.endpoint, params.payload);
40922
41174
  const reservation = params.endpoint.startsWith("terminal/") ? this.terminal.check(params.endpoint, params.payload) : void 0;
40923
41175
  let codexResult;
40924
41176
  if (this.codexWorkspace !== void 0) {
@@ -40935,17 +41187,25 @@ var HarnessRemoteBridge = class {
40935
41187
  if (params.endpoint === "session/canOpenWorkspacePath") {
40936
41188
  return { ok: true, value: true };
40937
41189
  }
41190
+ if (params.endpoint === "workspace/create") {
41191
+ const request = requestArgs(params.payload);
41192
+ const folder = typeof request.path === "string" ? request.path : "";
41193
+ await ensureHostWorkspaceDirectory(folder);
41194
+ }
40938
41195
  const startedAt = performance.now();
40939
41196
  const signal = AbortSignal.timeout(6e4);
40940
41197
  try {
40941
41198
  const nativePayload = normalizeWorkspaceRequestPayload(params.endpoint, params.payload, this.harnessVersion);
40942
41199
  const nativeResult = params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : this.terminalCarrier !== void 0 && (params.endpoint === "terminal/write" || params.endpoint === "terminal/resize") ? await this.terminalCarrier.call(params.endpoint, nativePayload, signal) : await this.gateway.dispatch(params.endpoint, nativePayload, signal);
40943
- const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
41200
+ const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : filterRemotePluginInventory(params.endpoint, nativeResult);
40944
41201
  this.logger?.debug("harness remote call ok", {
40945
41202
  endpoint: params.endpoint,
40946
41203
  durationMs: Math.round(performance.now() - startedAt)
40947
41204
  });
40948
- const wireResult = normalizeWorkspaceBinaryResult2(params.endpoint, result);
41205
+ const wireResult = normalizeWorkspaceBinaryResult2(
41206
+ params.endpoint,
41207
+ this.compactHistoryEnabled && ["session/page", "session/history", "subagent/history"].includes(params.endpoint) ? compactHistory(result) : result
41208
+ );
40949
41209
  return reservation === void 0 ? wireResult : this.terminal.result(params.endpoint, params.payload, wireResult, reservation);
40950
41210
  } catch (error) {
40951
41211
  if (reservation !== void 0) this.terminal.result(params.endpoint, params.payload, { ok: false, error: { code: "FAILED", message: "", details: {} } }, reservation);
@@ -41075,6 +41335,7 @@ var HarnessRemoteBridge = class {
41075
41335
  async openStream(input3) {
41076
41336
  const params = streamOpenSchema4.parse(input3);
41077
41337
  this.assertAllowed(params.endpoint);
41338
+ if (params.endpoint.startsWith("pluginManager/")) throw new RpcError("METHOD_NOT_ALLOWED", "Plugin management endpoints are not streams.");
41078
41339
  if (params.endpoint.startsWith("settings/")) throw new RpcError("METHOD_NOT_ALLOWED", "Settings endpoints are not streams.");
41079
41340
  if (TERMINAL_CALLS.has(params.endpoint)) throw new RpcError("METHOD_NOT_ALLOWED", "This terminal endpoint is not a stream.");
41080
41341
  if (TERMINAL_STREAMS.has(params.endpoint)) this.terminal.check(params.endpoint, params.payload);
@@ -41093,7 +41354,7 @@ var HarnessRemoteBridge = class {
41093
41354
  controller.abort();
41094
41355
  throw error;
41095
41356
  }
41096
- void this.pump(params.streamId, source, controller.signal);
41357
+ void this.pump(params.streamId, source, controller.signal, params.endpoint === "session/follow");
41097
41358
  return { opened: true, streamId: params.streamId };
41098
41359
  }
41099
41360
  closeStream(input3) {
@@ -41113,6 +41374,25 @@ var HarnessRemoteBridge = class {
41113
41374
  for (const [, stream] of streams) stream.controller.abort(reason);
41114
41375
  await Promise.all([this.gateway.dispose?.(), this.codexWorkspace?.closeAll()]);
41115
41376
  }
41377
+ async checkPluginManagement(endpoint, payload) {
41378
+ if (endpoint !== "pluginManager/setPluginEnabled" && endpoint !== "pluginManager/setBundleEnabled") return;
41379
+ const plugin = endpoint === "pluginManager/setPluginEnabled";
41380
+ const key = plugin ? "id" : "name";
41381
+ const parsed = external_exports.object({ args: external_exports.object({
41382
+ [key]: external_exports.string().min(1).max(4096),
41383
+ enabled: external_exports.boolean()
41384
+ }).strict() }).strict().safeParse(payload);
41385
+ if (!parsed.success) throw new RpcError("INVALID_MESSAGE", "Invalid plugin enablement request.");
41386
+ const args = parsed.data.args;
41387
+ const result = await this.gateway.dispatch(plugin ? "pluginManager/listPlugins" : "pluginManager/listBundles", { args: {} }, AbortSignal.timeout(6e4));
41388
+ if (!result.ok || !Array.isArray(result.value)) throw new RpcError("PERMISSION_DENIED", "Cannot verify the Host plugin inventory.");
41389
+ const row = result.value.find((value) => isRecord21(value) && value[plugin ? "entryId" : "name"] === args[key]);
41390
+ if (!isRecord21(row) || row.readOnlyReason !== void 0) throw new RpcError("PERMISSION_DENIED", "The Host plugin cannot be changed remotely.");
41391
+ const remoteModule = (value) => typeof value === "string" && /^(?:ds-harness-remote|dsh-remote)(?:\/|$)/.test(value);
41392
+ if (remoteModule(plugin ? row.moduleName : row.name) || !plugin && (!Array.isArray(row.rows) || row.rows.some((value) => !isRecord21(value) || remoteModule(value.moduleName)) || Array.isArray(row.overrides) && row.overrides.some((value) => remoteModule(value) || value === "ds-harness-remote-tui"))) {
41393
+ throw new RpcError("PERMISSION_DENIED", "The Remote plugin can only be enabled or disabled locally on the Host.");
41394
+ }
41395
+ }
41116
41396
  assertAllowed(endpoint) {
41117
41397
  if (!allowedEndpoints.has(endpoint) && !TERMINAL_CALLS.has(endpoint) && !TERMINAL_STREAMS.has(endpoint)) {
41118
41398
  throw new RpcError("METHOD_NOT_ALLOWED", "The requested Harness Remote endpoint is not allowed.");
@@ -41121,7 +41401,7 @@ var HarnessRemoteBridge = class {
41121
41401
  throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not support Remote event transport.");
41122
41402
  }
41123
41403
  }
41124
- async pump(streamId, source, signal) {
41404
+ async pump(streamId, source, signal, historyFollow = false) {
41125
41405
  let reason = "completed";
41126
41406
  let failure4;
41127
41407
  try {
@@ -41130,7 +41410,7 @@ var HarnessRemoteBridge = class {
41130
41410
  const frame = {
41131
41411
  streamId,
41132
41412
  hasValue: true,
41133
- ...value === void 0 ? {} : { value }
41413
+ ...value === void 0 ? {} : { value: historyFollow && this.compactHistoryEnabled && isCompactSnapshot(value) ? compactHistory(value) : value }
41134
41414
  };
41135
41415
  if (encodeMessage(createEvent("harness.remote.frame", frame)).byteLength > MAX_SECURE_MESSAGE_BYTES) {
41136
41416
  throw new Error("Oversized Harness Remote frame");
@@ -41258,6 +41538,21 @@ function needsDirectoryFallback(result) {
41258
41538
  const message = result.error.message.toLocaleLowerCase();
41259
41539
  return code.includes("capability") || code === "directory-picker/unavailable" || code === "directory-picker-unavailable" || message.includes("browse capability") || message.includes("browser capability") || message.includes("brower capability") || message.includes("directory-picker-unavailable");
41260
41540
  }
41541
+ function filterRemotePluginInventory(endpoint, result) {
41542
+ if (!result.ok) return result;
41543
+ const isRemote = (value) => typeof value === "string" && /^(?:ds-harness-remote|dsh-remote)(?:\/|$)/.test(value);
41544
+ const filterEntries = (entries) => entries.filter((entry) => !isRecord21(entry) || !isRemote(entry.moduleName));
41545
+ if (endpoint === "pluginManager/listPlugins" && Array.isArray(result.value)) {
41546
+ return { ...result, value: filterEntries(result.value) };
41547
+ }
41548
+ if (endpoint === "pluginManager/listBundles" && Array.isArray(result.value)) {
41549
+ return { ...result, value: result.value.filter((bundle) => !isRecord21(bundle) || !isRemote(bundle.name)).map((bundle) => isRecord21(bundle) && Array.isArray(bundle.rows) ? { ...bundle, rows: filterEntries(bundle.rows) } : bundle) };
41550
+ }
41551
+ if (endpoint === "pluginInventory/list" && isRecord21(result.value) && Array.isArray(result.value.entries)) {
41552
+ return { ...result, value: { ...result.value, entries: filterEntries(result.value.entries) } };
41553
+ }
41554
+ return result;
41555
+ }
41261
41556
  function requestArgs(payload) {
41262
41557
  const root = record8(payload);
41263
41558
  const args = isRecord21(root.args) ? root.args : root;
@@ -41269,6 +41564,9 @@ function record8(value) {
41269
41564
  function isRecord21(value) {
41270
41565
  return typeof value === "object" && value !== null && !Array.isArray(value);
41271
41566
  }
41567
+ function isCompactSnapshot(value) {
41568
+ return value !== null && typeof value === "object" && value.type === "snapshot";
41569
+ }
41272
41570
 
41273
41571
  // src/service.ts
41274
41572
  import { execFileSync as execFileSync2 } from "node:child_process";
@@ -41662,7 +41960,7 @@ var HostPluginRuntime = class {
41662
41960
  let reportedVersion;
41663
41961
  let errorCode6;
41664
41962
  try {
41665
- const response = await this.apiProxy?.host.describe({ rpcId: randomUUID6(), payload: {} });
41963
+ const response = await this.apiProxy?.host.describe({ rpcId: randomUUID7(), payload: {} });
41666
41964
  if (response === void 0) throw new Error("ApiProxy is unavailable");
41667
41965
  if (!response.result.ok) {
41668
41966
  errorCode6 = response.result.error.code;
@@ -42041,16 +42339,16 @@ async function renderTerminalQr(url) {
42041
42339
  async function renderCompactTerminalQr(url) {
42042
42340
  const { default: QRCode } = await import("qrcode");
42043
42341
  const modules = QRCode.create(url, { errorCorrectionLevel: "M" }).modules;
42044
- const size = modules.size + TERMINAL_QR_MARGIN * 2;
42342
+ const size2 = modules.size + TERMINAL_QR_MARGIN * 2;
42045
42343
  const lines = [];
42046
42344
  const isBlack = (row, column) => {
42047
42345
  const qrRow = row - TERMINAL_QR_MARGIN;
42048
42346
  const qrColumn = column - TERMINAL_QR_MARGIN;
42049
42347
  return qrRow >= 0 && qrRow < modules.size && qrColumn >= 0 && qrColumn < modules.size && modules.get(qrRow, qrColumn) === 1;
42050
42348
  };
42051
- for (let row = 0; row < size; row += 2) {
42349
+ for (let row = 0; row < size2; row += 2) {
42052
42350
  let line = "\x1B[30;47m";
42053
- for (let column = 0; column < size; column += 1) {
42351
+ for (let column = 0; column < size2; column += 1) {
42054
42352
  const top = isBlack(row, column);
42055
42353
  const bottom = isBlack(row + 1, column);
42056
42354
  line += top ? bottom ? "\u2588" : "\u2580" : bottom ? "\u2584" : " ";
@@ -42704,9 +43002,6 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
42704
43002
  console.error(message);
42705
43003
  }
42706
43004
  }, config.logLevel);
42707
- const hostIdentities = new IdentityStore({
42708
- directory: config.serverUrl === void 0 ? defaultIdentityDirectory : serverStorageDirectory(defaultIdentityDirectory, config.serverUrl, "host")
42709
- });
42710
43005
  const apiProxy = ctx.get("apiProxy");
42711
43006
  const nativeTypertGateway = ctx.get("typertGateway");
42712
43007
  const hostGateway = new TypertGatewaySwitch(nativeTypertGateway);
@@ -42725,56 +43020,103 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
42725
43020
  };
42726
43021
  }
42727
43022
  const subprocess = ctx.get("subprocess", false);
42728
- const runtime = new HostPluginRuntime(
42729
- config,
42730
- hostIdentities,
42731
- apiProxy,
42732
- logger,
42733
- localTypertGateway,
42734
- () => ctx.get("fileViewerHost"),
42735
- subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess),
42736
- (peer) => createPluginRouteDispatcher(
42737
- () => ctx.get("connection"),
42738
- () => ctx.get("webServer")?.port,
42739
- peer
42740
- )
42741
- );
42742
- let clientRuntime;
42743
- const hostControl = runtime;
42744
- if (config.serverUrl !== void 0 && connection !== void 0) {
42745
- const clientIdentities = new IdentityStore({
42746
- directory: serverStorageDirectory(defaultIdentityDirectory, config.serverUrl, "client")
43023
+ const createRuntimes = (config2) => {
43024
+ const hostIdentities = new IdentityStore({
43025
+ directory: config2.serverUrl === void 0 ? defaultIdentityDirectory : serverStorageDirectory(defaultIdentityDirectory, config2.serverUrl, "host")
42747
43026
  });
42748
- clientRuntime = new ClientModeRuntime(
42749
- config,
42750
- clientIdentities,
42751
- new ClientServerApi(config.serverUrl, new ServerCredentialStore(clientIdentities.directory)),
43027
+ const runtime2 = new HostPluginRuntime(
43028
+ config2,
43029
+ hostIdentities,
42752
43030
  apiProxy,
42753
- nativeTypertGateway,
42754
43031
  logger,
42755
- hostControl
43032
+ localTypertGateway,
43033
+ () => ctx.get("fileViewerHost"),
43034
+ subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess),
43035
+ (peer) => createPluginRouteDispatcher(
43036
+ () => ctx.get("connection"),
43037
+ () => ctx.get("webServer")?.port,
43038
+ peer
43039
+ )
42756
43040
  );
42757
- }
42758
- const controlRuntime = connection === void 0 ? void 0 : new PluginControlRuntime(config, defaultIdentityDirectory, settingsBinding, clientRuntime, hostControl);
43041
+ let clientRuntime2;
43042
+ if (config2.serverUrl !== void 0 && connection !== void 0) {
43043
+ const clientIdentities = new IdentityStore({
43044
+ directory: serverStorageDirectory(defaultIdentityDirectory, config2.serverUrl, "client")
43045
+ });
43046
+ clientRuntime2 = new ClientModeRuntime(
43047
+ config2,
43048
+ clientIdentities,
43049
+ new ClientServerApi(config2.serverUrl, new ServerCredentialStore(clientIdentities.directory)),
43050
+ apiProxy,
43051
+ nativeTypertGateway,
43052
+ logger,
43053
+ runtime2
43054
+ );
43055
+ }
43056
+ return { runtime: runtime2, clientRuntime: clientRuntime2 };
43057
+ };
43058
+ let { runtime, clientRuntime } = createRuntimes(config);
43059
+ let closed = false;
43060
+ let clientServiceProvided = clientRuntime !== void 0;
43061
+ let startup;
43062
+ let replacement;
43063
+ const replaceServer = (next) => {
43064
+ const operation = async () => {
43065
+ await startup;
43066
+ if (closed) throw new Error("Remote plugin is closed");
43067
+ const shutdown = await Promise.allSettled([clientRuntime?.close(), runtime.close()]);
43068
+ const failure4 = shutdown.find((result) => result.status === "rejected");
43069
+ if (failure4?.status === "rejected") throw failure4.reason;
43070
+ if (closed) throw new Error("Remote plugin is closed");
43071
+ const fresh = createRuntimes(next);
43072
+ try {
43073
+ await fresh.runtime.start();
43074
+ await fresh.clientRuntime?.start();
43075
+ if (closed) throw new Error("Remote plugin is closed");
43076
+ } catch (error) {
43077
+ await Promise.allSettled([fresh.clientRuntime?.close(), fresh.runtime.close()]);
43078
+ throw error;
43079
+ }
43080
+ runtime = fresh.runtime;
43081
+ clientRuntime = fresh.clientRuntime;
43082
+ ctx.dshRemote = runtime;
43083
+ if (clientRuntime !== void 0) {
43084
+ if (clientServiceProvided) ctx.dshRemoteClient = clientRuntime;
43085
+ else {
43086
+ ctx.provide("dshRemoteClient", clientRuntime);
43087
+ clientServiceProvided = true;
43088
+ }
43089
+ }
43090
+ tuiTarget = { runtime, config: next };
43091
+ if (tuiBinding !== void 0) tuiBinding.target = tuiTarget;
43092
+ return { client: clientRuntime, host: runtime };
43093
+ };
43094
+ replacement = operation();
43095
+ return replacement;
43096
+ };
43097
+ const controlRuntime = connection === void 0 ? void 0 : new PluginControlRuntime(config, defaultIdentityDirectory, settingsBinding, clientRuntime, runtime, replaceServer);
42759
43098
  ctx.provide("dshRemote", runtime);
42760
43099
  if (clientRuntime !== void 0) ctx.provide("dshRemoteClient", clientRuntime);
42761
- const tuiTarget = { runtime, config };
43100
+ let tuiTarget = { runtime, config };
42762
43101
  if (tuiBinding !== void 0) tuiBinding.target = tuiTarget;
42763
43102
  await disableLegacyLoaderEntries(ctx, logger);
42764
43103
  await ctx.effect(async () => {
42765
43104
  const disposeControl = controlRuntime?.register(connection, webServer);
42766
43105
  try {
42767
- await runtime.start();
42768
- if (clientRuntime !== void 0) {
42769
- await clientRuntime.start();
42770
- } else {
42771
- logger.warn("client remote mode is unavailable", {
42772
- serverConfigured: config.serverUrl !== void 0,
42773
- apiProxyAvailable: apiProxy !== void 0,
42774
- remoteGatewayAvailable: localTypertGateway.supportsCarrier,
42775
- connectionAvailable: connection !== void 0
42776
- });
42777
- }
43106
+ startup = (async () => {
43107
+ await runtime.start();
43108
+ if (clientRuntime !== void 0) {
43109
+ await clientRuntime.start();
43110
+ } else {
43111
+ logger.warn("client remote mode is unavailable", {
43112
+ serverConfigured: config.serverUrl !== void 0,
43113
+ apiProxyAvailable: apiProxy !== void 0,
43114
+ remoteGatewayAvailable: localTypertGateway.supportsCarrier,
43115
+ connectionAvailable: connection !== void 0
43116
+ });
43117
+ }
43118
+ })();
43119
+ await startup;
42778
43120
  } catch (error) {
42779
43121
  if (tuiBinding?.target === tuiTarget) tuiBinding.target = void 0;
42780
43122
  await disposeControl?.();
@@ -42783,6 +43125,8 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
42783
43125
  throw error;
42784
43126
  }
42785
43127
  return async () => {
43128
+ closed = true;
43129
+ await replacement?.catch(() => void 0);
42786
43130
  if (tuiBinding?.target === tuiTarget) tuiBinding.target = void 0;
42787
43131
  await disposeControl?.();
42788
43132
  await clientRuntime?.close();