@blue-soda/dsh-remote 0.5.7 → 0.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,17 +4,10 @@ var __export = (target2, all) => {
4
4
  __defProp(target2, name2, { get: all[name2], enumerable: true });
5
5
  };
6
6
 
7
- // ../webrtc/dist/websocket-backpressure.js
8
- async function waitForRelayCapacity(socket) {
9
- const started = Date.now();
10
- while ((socket.bufferedAmount ?? 0) > 512 * 1024) {
11
- if (socket.readyState !== 1 || Date.now() - started > 1e4)
12
- throw new Error("Relay consumer is too slow or disconnected");
13
- await new Promise((resolve4) => setTimeout(resolve4, 10));
14
- }
15
- if (socket.readyState !== 1)
16
- throw new Error("Relay transport closed");
17
- }
7
+ // src/client-runtime.ts
8
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
9
+ import { homedir as homedir2 } from "node:os";
10
+ import { join as join6 } from "node:path";
18
11
 
19
12
  // ../../node_modules/zod/v3/external.js
20
13
  var external_exports = {};
@@ -177,8 +170,8 @@ var util;
177
170
  return void 0;
178
171
  };
179
172
  util2.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val;
180
- function joinValues(array3, separator = " | ") {
181
- return array3.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator);
173
+ function joinValues(array2, separator = " | ") {
174
+ return array2.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator);
182
175
  }
183
176
  util2.joinValues = joinValues;
184
177
  util2.jsonStringifyReplacer = (_, value) => {
@@ -4594,6 +4587,32 @@ function cryptoRandomId() {
4594
4587
  return `msg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
4595
4588
  }
4596
4589
 
4590
+ // src/history-policy.ts
4591
+ var HISTORY_PAGE_SIZES = [50, 30, 20, 12, 6, 3, 1];
4592
+ var RESPONSE_ENVELOPE_BYTES = 8 * 1024;
4593
+ var HISTORY_BUDGET_BYTES = MAX_SECURE_MESSAGE_BYTES - RESPONSE_ENVELOPE_BYTES;
4594
+ function historyPageSizes(start = HISTORY_PAGE_SIZES[0]) {
4595
+ const sizes = [start];
4596
+ for (const size of HISTORY_PAGE_SIZES) if (size < start) sizes.push(size);
4597
+ return sizes;
4598
+ }
4599
+ function codexHistoryStatsFileEnabled(env = process.env) {
4600
+ const value = env.DSH_CODEX_HISTORY_STATS?.trim().toLowerCase();
4601
+ return value === "1" || value === "true" || value === "on" || value === "yes";
4602
+ }
4603
+
4604
+ // ../webrtc/dist/websocket-backpressure.js
4605
+ async function waitForRelayCapacity(socket) {
4606
+ const started = Date.now();
4607
+ while ((socket.bufferedAmount ?? 0) > 512 * 1024) {
4608
+ if (socket.readyState !== 1 || Date.now() - started > 1e4)
4609
+ throw new Error("Relay consumer is too slow or disconnected");
4610
+ await new Promise((resolve5) => setTimeout(resolve5, 10));
4611
+ }
4612
+ if (socket.readyState !== 1)
4613
+ throw new Error("Relay transport closed");
4614
+ }
4615
+
4597
4616
  // ../webrtc/dist/transport.js
4598
4617
  var BaseTransport = class {
4599
4618
  handlers = /* @__PURE__ */ new Set();
@@ -5066,8 +5085,8 @@ var RtcDataChannelTransport = class {
5066
5085
  if (this.connectPromise !== void 0)
5067
5086
  return this.connectPromise;
5068
5087
  this.armAbort(signal);
5069
- this.connectPromise = new Promise((resolve4, reject) => {
5070
- this.openResolve = resolve4;
5088
+ this.connectPromise = new Promise((resolve5, reject) => {
5089
+ this.openResolve = resolve5;
5071
5090
  this.openReject = reject;
5072
5091
  this.negotiateTimer = setTimeout(() => {
5073
5092
  void this.failOpenAfterStats(new RtcConnectError("RTC_CONNECT_TIMEOUT", `WebRTC negotiation timed out after ${this.negotiateTimeoutMs}ms.`));
@@ -5282,9 +5301,9 @@ var RtcDataChannelTransport = class {
5282
5301
  if (this.closed || this.opened)
5283
5302
  return;
5284
5303
  this.opened = true;
5285
- const resolve4 = this.openResolve;
5304
+ const resolve5 = this.openResolve;
5286
5305
  this.clearNegotiation();
5287
- void this.resolveSelectedTransport().then(() => resolve4?.());
5306
+ void this.resolveSelectedTransport().then(() => resolve5?.());
5288
5307
  };
5289
5308
  channel.onmessage = (event) => {
5290
5309
  if (this.closed || !this.opened)
@@ -5678,7 +5697,7 @@ function asError(error) {
5678
5697
  return error instanceof Error ? error : new RtcConnectError("RTC_FAILED", "WebRTC negotiation failed.");
5679
5698
  }
5680
5699
  function sleep(ms) {
5681
- return new Promise((resolve4) => setTimeout(resolve4, ms));
5700
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
5682
5701
  }
5683
5702
 
5684
5703
  // ../webrtc/dist/adaptive-transport.js
@@ -5719,8 +5738,8 @@ var AdaptiveTransport = class extends BaseTransport {
5719
5738
  this.socket = new WebSocket(this.url);
5720
5739
  this.controlFrameLimits = {};
5721
5740
  this.socket.binaryType = "arraybuffer";
5722
- await new Promise((resolve4, reject) => {
5723
- this.readyResolve = resolve4;
5741
+ await new Promise((resolve5, reject) => {
5742
+ this.readyResolve = resolve5;
5724
5743
  this.readyReject = reject;
5725
5744
  this.handshakeTimer = setTimeout(() => this.failConnection(new Error("Adaptive control handshake timed out")), this.options.handshakeTimeoutMs ?? 15e3);
5726
5745
  const socket = this.socket;
@@ -6242,7 +6261,7 @@ function hexToBytes(hex) {
6242
6261
  const al = hl / 2;
6243
6262
  if (hl % 2)
6244
6263
  throw new Error("hex string expected, got unpadded hex of length " + hl);
6245
- const array3 = new Uint8Array(al);
6264
+ const array2 = new Uint8Array(al);
6246
6265
  for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
6247
6266
  const n1 = asciiToBase16(hex.charCodeAt(hi));
6248
6267
  const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
@@ -6250,9 +6269,9 @@ function hexToBytes(hex) {
6250
6269
  const char = hex[hi] + hex[hi + 1];
6251
6270
  throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
6252
6271
  }
6253
- array3[ai] = n1 * 16 + n2;
6272
+ array2[ai] = n1 * 16 + n2;
6254
6273
  }
6255
- return array3;
6274
+ return array2;
6256
6275
  }
6257
6276
  function utf8ToBytes(str) {
6258
6277
  if (typeof str !== "string")
@@ -8959,7 +8978,7 @@ function hexToBytes2(hex) {
8959
8978
  const al = hl / 2;
8960
8979
  if (hl % 2)
8961
8980
  throw new RangeError("hex string expected, got unpadded hex of length " + hl);
8962
- const array3 = new Uint8Array(al);
8981
+ const array2 = new Uint8Array(al);
8963
8982
  for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
8964
8983
  const n1 = asciiToBase162(hex.charCodeAt(hi));
8965
8984
  const n2 = asciiToBase162(hex.charCodeAt(hi + 1));
@@ -8967,9 +8986,9 @@ function hexToBytes2(hex) {
8967
8986
  const char = hex[hi] + hex[hi + 1];
8968
8987
  throw new RangeError('hex string expected, got non-hex character "' + char + '" at index ' + hi);
8969
8988
  }
8970
- array3[ai] = n1 * 16 + n2;
8989
+ array2[ai] = n1 * 16 + n2;
8971
8990
  }
8972
- return array3;
8991
+ return array2;
8973
8992
  }
8974
8993
  function concatBytes2(...arrays) {
8975
8994
  let sum = 0;
@@ -13213,8 +13232,8 @@ function errorCode(error) {
13213
13232
  return typeof code === "string" ? code : void 0;
13214
13233
  }
13215
13234
  function sleep2(ms) {
13216
- return new Promise((resolve4) => {
13217
- setTimeout(resolve4, ms);
13235
+ return new Promise((resolve5) => {
13236
+ setTimeout(resolve5, ms);
13218
13237
  });
13219
13238
  }
13220
13239
 
@@ -13279,7 +13298,7 @@ var ServerCredentialStore = class {
13279
13298
  if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") throw error;
13280
13299
  if (await reclaimAbandonedRefreshLock(lock)) continue;
13281
13300
  if (Date.now() >= deadline) throw new ServerCredentialsBusyError();
13282
- await new Promise((resolve4) => setTimeout(resolve4, 50));
13301
+ await new Promise((resolve5) => setTimeout(resolve5, 50));
13283
13302
  }
13284
13303
  }
13285
13304
  try {
@@ -13418,7 +13437,8 @@ var entryConfigSchema = s.object({
13418
13437
  ]),
13419
13438
  codex: s.object({
13420
13439
  enabled: s.boolean(),
13421
- binary: s.string()
13440
+ binary: s.string(),
13441
+ showUnprojectedSessions: s.boolean()
13422
13442
  }),
13423
13443
  acp: s.object({ enabled: s.boolean(), backends: s.array(s.object({ id: s.string(), enabled: s.boolean(), command: s.string(), args: s.array(s.string()), cwd: s.string() })) })
13424
13444
  });
@@ -13451,7 +13471,8 @@ var configSchema = external_exports.object({
13451
13471
  reconnect: reconnectSchema.optional(),
13452
13472
  codex: external_exports.object({
13453
13473
  enabled: external_exports.boolean().optional(),
13454
- binary: external_exports.string().trim().min(1).max(4096).optional()
13474
+ binary: external_exports.string().trim().min(1).max(4096).optional(),
13475
+ showUnprojectedSessions: external_exports.boolean().optional()
13455
13476
  }).strict().optional(),
13456
13477
  acp: external_exports.object({ enabled: external_exports.boolean().optional(), backends: external_exports.array(external_exports.object({ id: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,31}$/i), enabled: external_exports.boolean().optional(), command: external_exports.string().trim().min(1).max(4096).optional(), args: external_exports.array(external_exports.string().max(4096)).max(32).optional(), cwd: external_exports.string().max(4096).optional() }).strict()).max(12).optional(), backend: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,31}$/i).optional(), command: external_exports.string().trim().min(1).max(4096).optional(), args: external_exports.array(external_exports.string().max(4096)).max(32).optional(), cwd: external_exports.string().max(4096).optional() }).strict().optional()
13457
13478
  }).strict();
@@ -13493,7 +13514,8 @@ function resolveConfig(input2 = {}, env = process.env) {
13493
13514
  // an account and, while unavailable, retries every minute. The bundled profile patch says the same thing, so
13494
13515
  // an install that never touches the switch has it off.
13495
13516
  enabled: parsed.codex?.enabled ?? false,
13496
- binary: parsed.codex?.binary ?? "codex"
13517
+ binary: parsed.codex?.binary ?? "codex",
13518
+ showUnprojectedSessions: parsed.codex?.showUnprojectedSessions ?? true
13497
13519
  },
13498
13520
  acp: { enabled: parsed.acp?.enabled ?? true, backends: [.../* @__PURE__ */ new Set(["codex", "cursor", "kimi", ...parsed.acp?.backends?.map((item) => item.id) ?? []])].map((id4) => {
13499
13521
  const d = parsed.acp?.backends?.find((x) => x.id === id4);
@@ -13521,7 +13543,7 @@ function normalizeServerUrl(value) {
13521
13543
  }
13522
13544
 
13523
13545
  // src/version.ts
13524
- var PLUGIN_VERSION = "0.5.7";
13546
+ var PLUGIN_VERSION = "0.5.9";
13525
13547
 
13526
13548
  // src/server-api.ts
13527
13549
  var ENABLED_QR_PROVIDERS = ["github"];
@@ -14144,7 +14166,7 @@ var HostServerConnection = class {
14144
14166
  this.controlFrameLimits = {};
14145
14167
  let acknowledged = false;
14146
14168
  let messageQueue = Promise.resolve();
14147
- await new Promise((resolve4, reject) => {
14169
+ await new Promise((resolve5, reject) => {
14148
14170
  let settled = false;
14149
14171
  const helloTimer = setTimeout(() => socket.close(4001, "hello timeout"), 1e4);
14150
14172
  const finish = (error) => {
@@ -14153,7 +14175,7 @@ var HostServerConnection = class {
14153
14175
  clearTimeout(helloTimer);
14154
14176
  this.online = false;
14155
14177
  if (this.socket === socket) this.socket = void 0;
14156
- void this.dropTunnels().finally(() => error === void 0 ? resolve4() : reject(error));
14178
+ void this.dropTunnels().finally(() => error === void 0 ? resolve5() : reject(error));
14157
14179
  };
14158
14180
  socket.onopen = () => {
14159
14181
  this.sendControl("hello", {
@@ -14713,14 +14735,14 @@ var HostServerConnection = class {
14713
14735
  waitBeforeRetry(baseDelay, exact = false) {
14714
14736
  const spread = exact ? 0 : baseDelay * this.config.reconnect.jitter;
14715
14737
  const delay = Math.max(0, Math.round(baseDelay - spread + Math.random() * spread * 2));
14716
- return new Promise((resolve4) => {
14738
+ return new Promise((resolve5) => {
14717
14739
  const timer = setTimeout(() => {
14718
14740
  this.retryWake = void 0;
14719
- resolve4();
14741
+ resolve5();
14720
14742
  }, delay);
14721
14743
  this.retryWake = () => {
14722
14744
  clearTimeout(timer);
14723
- resolve4();
14745
+ resolve5();
14724
14746
  };
14725
14747
  });
14726
14748
  }
@@ -14731,8 +14753,8 @@ async function drainWithin(promise, ms) {
14731
14753
  try {
14732
14754
  await Promise.race([
14733
14755
  promise.catch(() => void 0),
14734
- new Promise((resolve4) => {
14735
- timer = setTimeout(resolve4, ms);
14756
+ new Promise((resolve5) => {
14757
+ timer = setTimeout(resolve5, ms);
14736
14758
  timer.unref?.();
14737
14759
  })
14738
14760
  ]);
@@ -15086,7 +15108,7 @@ var LoopbackHost = class {
15086
15108
  const response = handle.response;
15087
15109
  let chunk = response.read(Math.min(response.readableLength || LOOPBACK_CHUNK_BYTES, LOOPBACK_CHUNK_BYTES));
15088
15110
  if (chunk === null && !response.readableEnded && !response.destroyed) {
15089
- await new Promise((resolve4, reject) => {
15111
+ await new Promise((resolve5, reject) => {
15090
15112
  const timer = setTimeout(() => finish(new Error("timeout")), 2e4);
15091
15113
  const ready = () => finish();
15092
15114
  const fail4 = () => finish(new Error("closed"));
@@ -15096,7 +15118,7 @@ var LoopbackHost = class {
15096
15118
  response.off("end", ready);
15097
15119
  response.off("close", ready);
15098
15120
  response.off("error", fail4);
15099
- error === void 0 ? resolve4() : reject(error);
15121
+ error === void 0 ? resolve5() : reject(error);
15100
15122
  };
15101
15123
  response.once("readable", ready);
15102
15124
  response.once("end", ready);
@@ -15127,22 +15149,22 @@ var LoopbackHost = class {
15127
15149
  this.close(value.id);
15128
15150
  throw new RpcError("RATE_LIMITED", "Preview WebSocket buffer exceeded its limit.");
15129
15151
  }
15130
- await new Promise((resolve4, reject) => handle.socket.send(data2, { binary: value.binary }, (error) => error ? reject(new RpcError("LOOPBACK_CLOSED", "Preview socket closed.")) : resolve4()));
15152
+ await new Promise((resolve5, reject) => handle.socket.send(data2, { binary: value.binary }, (error) => error ? reject(new RpcError("LOOPBACK_CLOSED", "Preview socket closed.")) : resolve5()));
15131
15153
  return { sent: true };
15132
15154
  }
15133
15155
  if (value.op === "ws.read") {
15134
15156
  if (handle.reading) throw new RpcError("REQUEST_CONFLICT", "Only one preview read may be pending.");
15135
15157
  handle.reading = true;
15136
15158
  try {
15137
- if (handle.messages.length === 0 && !handle.closed) await new Promise((resolve4) => {
15159
+ if (handle.messages.length === 0 && !handle.closed) await new Promise((resolve5) => {
15138
15160
  const timer = setTimeout(() => {
15139
15161
  handle.wake = void 0;
15140
- resolve4();
15162
+ resolve5();
15141
15163
  }, 2e4);
15142
15164
  handle.wake = () => {
15143
15165
  clearTimeout(timer);
15144
15166
  handle.wake = void 0;
15145
- resolve4();
15167
+ resolve5();
15146
15168
  };
15147
15169
  });
15148
15170
  let bytes = 0;
@@ -15189,7 +15211,7 @@ var LoopbackHost = class {
15189
15211
  openHttp(value) {
15190
15212
  const body = value.body === void 0 ? void 0 : Buffer.from(value.body, "base64");
15191
15213
  if ((body?.length ?? 0) > LOOPBACK_MAX_BODY_BYTES) throw new Error("body limit");
15192
- return new Promise((resolve4, reject) => {
15214
+ return new Promise((resolve5, reject) => {
15193
15215
  const headers = Object.fromEntries(cleanHeaders(value.headers));
15194
15216
  let handle;
15195
15217
  const request = httpRequest({
@@ -15208,7 +15230,7 @@ var LoopbackHost = class {
15208
15230
  handle.response = response;
15209
15231
  response.on("error", () => void 0);
15210
15232
  clearTimeout(timer);
15211
- resolve4({ status: response.statusCode ?? 502, headers: cleanHeaders(headerPairs(response.headers)) });
15233
+ resolve5({ status: response.statusCode ?? 502, headers: cleanHeaders(headerPairs(response.headers)) });
15212
15234
  });
15213
15235
  const timer = setTimeout(() => {
15214
15236
  request.destroy();
@@ -15224,7 +15246,7 @@ var LoopbackHost = class {
15224
15246
  });
15225
15247
  }
15226
15248
  openWs(value) {
15227
- return new Promise((resolve4, reject) => {
15249
+ return new Promise((resolve5, reject) => {
15228
15250
  const socket = new WebSocket2(`ws://127.0.0.1:${value.port}${value.path}`, value.protocols, {
15229
15251
  headers: Object.fromEntries(cleanHeaders(value.headers)),
15230
15252
  followRedirects: false,
@@ -15234,7 +15256,7 @@ var LoopbackHost = class {
15234
15256
  });
15235
15257
  const handle = { kind: "ws", port: value.port, socket, messages: [], bytes: 0, reading: false, closed: false, touched: Date.now() };
15236
15258
  this.handles.set(value.id, handle);
15237
- socket.once("open", () => resolve4({ protocol: socket.protocol }));
15259
+ socket.once("open", () => resolve5({ protocol: socket.protocol }));
15238
15260
  socket.on("message", (data2, binary) => {
15239
15261
  if (handle.closed) return;
15240
15262
  const message = { data: Buffer.from(data2).toString("base64"), binary };
@@ -15293,7 +15315,7 @@ var LoopbackPreview = class {
15293
15315
  for (const client of running.ws.clients) client.terminate();
15294
15316
  running.ws.close();
15295
15317
  running.server.closeAllConnections();
15296
- await new Promise((resolve4) => running.server.close(() => resolve4()));
15318
+ await new Promise((resolve5) => running.server.close(() => resolve5()));
15297
15319
  }
15298
15320
  this.servers.clear();
15299
15321
  }
@@ -15348,11 +15370,11 @@ var LoopbackPreview = class {
15348
15370
  void this.release(id4);
15349
15371
  });
15350
15372
  });
15351
- await new Promise((resolve4, reject) => {
15373
+ await new Promise((resolve5, reject) => {
15352
15374
  server.once("error", reject);
15353
15375
  server.listen(0, "127.0.0.1", () => {
15354
15376
  server.off("error", reject);
15355
- resolve4();
15377
+ resolve5();
15356
15378
  });
15357
15379
  });
15358
15380
  const address = server.address();
@@ -15418,10 +15440,10 @@ var LoopbackPreview = class {
15418
15440
  while (!signal.aborted) {
15419
15441
  const chunk = await this.client.rpc("loopback.call", { op: "http.read", id: id4 }, signal);
15420
15442
  if (chunk.data !== "" && !res.write(Buffer.from(chunk.data, "base64"))) {
15421
- await new Promise((resolve4, reject) => {
15443
+ await new Promise((resolve5, reject) => {
15422
15444
  const ready = () => {
15423
15445
  cleanup();
15424
- resolve4();
15446
+ resolve5();
15425
15447
  };
15426
15448
  const closed = () => {
15427
15449
  cleanup();
@@ -15481,7 +15503,7 @@ var LoopbackPreview = class {
15481
15503
  const result = await this.client.rpc("loopback.call", { op: "ws.read", id: id4 }, signal);
15482
15504
  for (const message of result.messages) {
15483
15505
  if (socket.bufferedAmount > 1024 * 1024) throw new Error("slow preview");
15484
- await new Promise((resolve4, reject) => socket.send(Buffer.from(message.data, "base64"), { binary: message.binary }, (error) => error ? reject(error) : resolve4()));
15506
+ await new Promise((resolve5, reject) => socket.send(Buffer.from(message.data, "base64"), { binary: message.binary }, (error) => error ? reject(error) : resolve5()));
15485
15507
  }
15486
15508
  if (result.closed) break;
15487
15509
  }
@@ -16169,13 +16191,13 @@ var RemoteClientCore = class {
16169
16191
  throw rpcAbortedError(method, signal.reason);
16170
16192
  const timeoutMs = callTimeoutMs(this.timeoutMs, options);
16171
16193
  const request = createRpcRequest(method, params);
16172
- const result = new Promise((resolve4, reject) => {
16194
+ const result = new Promise((resolve5, reject) => {
16173
16195
  const timer = setTimeout(() => {
16174
16196
  this.rejectPending(request.id, new RemoteClientError("RPC_TIMEOUT", `RPC ${method} timed out after ${timeoutMs}ms`));
16175
16197
  }, timeoutMs);
16176
16198
  const pending = {
16177
16199
  method,
16178
- resolve: resolve4,
16200
+ resolve: resolve5,
16179
16201
  reject,
16180
16202
  timer
16181
16203
  };
@@ -16484,7 +16506,7 @@ var ClientSecureTransport = class {
16484
16506
  }
16485
16507
  };
16486
16508
  async function waitForResponder(inner, noise) {
16487
- await new Promise((resolve4, reject) => {
16509
+ await new Promise((resolve5, reject) => {
16488
16510
  let settled = false;
16489
16511
  const timer = setTimeout(() => finish(new Error("Noise IK handshake timed out.")), 1e4);
16490
16512
  const unsubscribe = inner.onHandshake((step, data2) => {
@@ -16503,7 +16525,7 @@ async function waitForResponder(inner, noise) {
16503
16525
  settled = true;
16504
16526
  clearTimeout(timer);
16505
16527
  unsubscribe();
16506
- if (error === void 0) resolve4();
16528
+ if (error === void 0) resolve5();
16507
16529
  else reject(error);
16508
16530
  };
16509
16531
  void inner.sendHandshake(1, noise.writeHandshake()).catch((error) => {
@@ -17099,7 +17121,7 @@ var AsyncFrameQueue = class {
17099
17121
  continue;
17100
17122
  }
17101
17123
  if (this.closed) return;
17102
- const next = await new Promise((resolve4) => this.waiters.push(resolve4));
17124
+ const next = await new Promise((resolve5) => this.waiters.push(resolve5));
17103
17125
  if (next.done) return;
17104
17126
  yield next.value;
17105
17127
  }
@@ -17504,7 +17526,7 @@ var AsyncValueQueue = class {
17504
17526
  if (this.error !== void 0) throw this.error;
17505
17527
  return;
17506
17528
  }
17507
- const next = await new Promise((resolve4) => this.waiters.push(resolve4));
17529
+ const next = await new Promise((resolve5) => this.waiters.push(resolve5));
17508
17530
  if (next.done) {
17509
17531
  if (this.error !== void 0) throw this.error;
17510
17532
  return;
@@ -17595,6 +17617,11 @@ function patchWelcomeDescribe2(value) {
17595
17617
  return { ...value, namespaces };
17596
17618
  }
17597
17619
 
17620
+ // src/codex/virtual-harness.ts
17621
+ import { mkdirSync, writeFileSync } from "node:fs";
17622
+ import { homedir } from "node:os";
17623
+ import { join as join4 } from "node:path";
17624
+
17598
17625
  // src/codex/permissions.ts
17599
17626
  function codexPermissionPresetFromResponse(value) {
17600
17627
  const root = record(value);
@@ -17625,135 +17652,6 @@ function resolveCodexThreadKey(key) {
17625
17652
  return codexThreadIdFromKey(key) ?? key;
17626
17653
  }
17627
17654
 
17628
- // src/codex/method-policy.ts
17629
- var id2 = external_exports.string().min(1).max(256);
17630
- var cursor = external_exports.string().min(1).max(4096).nullable().optional();
17631
- var textInput = external_exports.object({
17632
- type: external_exports.literal("text"),
17633
- text: external_exports.string().min(1).max(256 * 1024)
17634
- }).strict();
17635
- var imageMediaType = external_exports.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]);
17636
- var canonicalBase64 = external_exports.string().min(4).max(288 * 1024 * 1024).refine((value) => value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(value), {
17637
- message: "Image data must use canonical base64."
17638
- });
17639
- var imageInput = external_exports.object({
17640
- type: external_exports.literal("image"),
17641
- mediaType: imageMediaType,
17642
- data: canonicalBase64
17643
- }).strict();
17644
- var input = external_exports.array(external_exports.union([textInput, imageInput])).min(1).max(16);
17645
- var permissionPreset = external_exports.enum(["workspace-write", "danger-full-access"]);
17646
- var projectRoot = external_exports.object({
17647
- path: external_exports.string().min(1).max(4096)
17648
- }).strict();
17649
- var CODEX_HISTORY_MAX_MESSAGES = 200;
17650
- var schemas = {
17651
- "account/read": external_exports.object({ refreshToken: external_exports.literal(false).optional() }).strict(),
17652
- "model/list": external_exports.object({
17653
- cursor,
17654
- limit: external_exports.number().int().min(1).max(100).optional(),
17655
- includeHidden: external_exports.boolean().optional()
17656
- }).strict(),
17657
- "project/list": external_exports.object({
17658
- cursor,
17659
- limit: external_exports.number().int().min(1).max(100).optional()
17660
- }).strict(),
17661
- "project/create": external_exports.object({
17662
- name: external_exports.string().trim().min(1).max(256),
17663
- roots: external_exports.array(projectRoot).length(1),
17664
- idempotencyKey: external_exports.string().min(16).max(256)
17665
- }).strict(),
17666
- "thread/list": external_exports.object({
17667
- cursor,
17668
- limit: external_exports.number().int().min(1).max(100).optional(),
17669
- sortKey: external_exports.enum(["created_at", "updated_at", "recency_at"]).optional(),
17670
- sortDirection: external_exports.enum(["asc", "desc"]).optional(),
17671
- modelProviders: external_exports.array(external_exports.string().min(1).max(128)).max(32).nullable().optional(),
17672
- sourceKinds: external_exports.array(external_exports.enum(["cli", "vscode", "exec", "appServer", "unknown"])).max(8).optional(),
17673
- archived: external_exports.boolean().optional(),
17674
- isPinned: external_exports.boolean().optional(),
17675
- cwd: external_exports.union([external_exports.string().min(1).max(4096), external_exports.array(external_exports.string().min(1).max(4096)).min(1).max(32)]).optional(),
17676
- useStateDbOnly: external_exports.boolean().optional(),
17677
- searchTerm: external_exports.string().max(1024).optional(),
17678
- // Upstream `ThreadListParams` declares these two as well. Refusing them turned a
17679
- // legitimate client query into "The CodeX call parameters are invalid.".
17680
- originators: external_exports.array(external_exports.string().min(1).max(256)).max(32).nullable().optional(),
17681
- sectionId: external_exports.string().max(256).nullable().optional()
17682
- }).strict(),
17683
- "thread/read": external_exports.object({ threadId: id2, includeTurns: external_exports.boolean().optional() }).strict(),
17684
- "dsh/sessionHistory": external_exports.object({
17685
- threadId: id2,
17686
- beforeSeq: external_exports.number().int().nonnegative().optional(),
17687
- throughSeq: external_exports.number().int().min(-1).optional(),
17688
- maxMessages: external_exports.number().int().min(1).max(CODEX_HISTORY_MAX_MESSAGES).optional()
17689
- }).strict(),
17690
- "dsh/directoryList": external_exports.object({
17691
- path: external_exports.string().min(1).max(4096)
17692
- }).strict(),
17693
- "thread/start": external_exports.object({
17694
- cwd: external_exports.string().min(1).max(4096),
17695
- model: external_exports.string().min(1).max(128).optional(),
17696
- personality: external_exports.string().min(1).max(64).optional(),
17697
- permissionPreset: permissionPreset.optional()
17698
- }).strict(),
17699
- "thread/resume": external_exports.object({
17700
- threadId: id2,
17701
- model: external_exports.string().min(1).max(128).optional(),
17702
- permissionPreset: permissionPreset.optional()
17703
- }).strict(),
17704
- "thread/fork": external_exports.object({
17705
- threadId: id2,
17706
- lastTurnId: id2.optional(),
17707
- permissionPreset: permissionPreset.optional()
17708
- }).strict(),
17709
- // Upstream declares a plain string and uses the empty value to clear a name, so the
17710
- // trim-based minimum rejected a legitimate rename.
17711
- "thread/name/set": external_exports.object({ threadId: id2, name: external_exports.string().max(256) }).strict(),
17712
- "thread/archive": external_exports.object({ threadId: id2 }).strict(),
17713
- "thread/unarchive": external_exports.object({ threadId: id2 }).strict(),
17714
- "thread/unsubscribe": external_exports.object({ threadId: id2 }).strict(),
17715
- "turn/start": external_exports.object({
17716
- threadId: id2,
17717
- input,
17718
- model: external_exports.string().min(1).max(128).optional(),
17719
- effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]).optional(),
17720
- summary: external_exports.enum(["auto", "concise", "detailed", "none"]).optional(),
17721
- personality: external_exports.string().min(1).max(64).optional(),
17722
- permissionPreset: permissionPreset.optional()
17723
- }).strict(),
17724
- "turn/steer": external_exports.object({
17725
- threadId: id2,
17726
- input,
17727
- expectedTurnId: id2,
17728
- // Declared by upstream `TurnSteerParams`; see the thread/list note above.
17729
- clientUserMessageId: external_exports.string().max(256).nullable().optional()
17730
- }).strict(),
17731
- "turn/interrupt": external_exports.object({ threadId: id2, turnId: id2 }).strict()
17732
- };
17733
- var CODEX_APP_ALLOWLIST = Object.freeze(Object.keys(schemas));
17734
- function parseCodexCall(method, params) {
17735
- if (!Object.prototype.hasOwnProperty.call(schemas, method)) {
17736
- throw new RpcError("METHOD_NOT_ALLOWED", "The requested Codex method is not available over Remote.");
17737
- }
17738
- const schema = schemas[method];
17739
- const parsed = schema.safeParse(params);
17740
- if (!parsed.success) {
17741
- const detail = parsed.error.issues.slice(0, 4).map((issue) => {
17742
- const path = issue.path.length === 0 ? "(root)" : issue.path.join(".");
17743
- const keys = "keys" in issue && Array.isArray(issue.keys) ? ` [${issue.keys.join(", ")}]` : "";
17744
- return `${path}${keys}: ${issue.code}`;
17745
- }).join(", ");
17746
- throw new RpcError("INVALID_MESSAGE", `The CodeX call parameters are invalid. (${method} \u2192 ${detail})`);
17747
- }
17748
- return { method, params: parsed.data };
17749
- }
17750
- function isThreadMutation(method) {
17751
- return method === "turn/start" || method === "turn/steer" || method === "turn/interrupt";
17752
- }
17753
- function threadIdFromParams(params) {
17754
- return typeof params.threadId === "string" ? params.threadId : void 0;
17755
- }
17756
-
17757
17655
  // src/codex/codex-image-codec.ts
17758
17656
  var CODEX_IMAGE_MEDIA_TYPES2 = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
17759
17657
  var CODEX_IMAGE_ATTACHMENT_PREFIX = "codex-image:";
@@ -17988,9 +17886,10 @@ var CODEX_MODEL = "codex";
17988
17886
  var CODEX_PAGE_LIMIT = 100;
17989
17887
  var MAX_CODEX_PAGES = 32;
17990
17888
  var MAX_LIVE_TOOL_OUTPUT = 128 * 1024;
17991
- var DEFAULT_HISTORY_MESSAGES = 50;
17992
17889
  var SESSION_SEARCH_RESULT_LIMIT = 20;
17993
17890
  var SESSION_SEARCH_SNIPPET_CODE_POINTS = 240;
17891
+ var HISTORY_PAGE_CACHE_TTL_MS = 6e4;
17892
+ var HISTORY_PAGE_CACHE_LIMIT = 24;
17994
17893
  var CODEX_DEFAULT_PERMISSION = "workspace-write";
17995
17894
  var MAX_CODEX_PROMPT_PARTS = 16;
17996
17895
  var MAX_CODEX_PROMPT_TEXT = 256 * 1024;
@@ -18037,6 +17936,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
18037
17936
  this.sessionGeneration = sessionGeneration;
18038
17937
  this.hostCarrier = hostCarrier;
18039
17938
  this.api = this.createApiProxy();
17939
+ this.installHistoryCacheInspector();
18040
17940
  this.stopCapabilityWatch = this.client.watchCapability?.((available) => {
18041
17941
  if (!available) this.onSessionLost?.("domain-notice");
18042
17942
  });
@@ -18056,6 +17956,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
18056
17956
  selectedModels = /* @__PURE__ */ new Map();
18057
17957
  selectedPermissions = /* @__PURE__ */ new Map();
18058
17958
  imageAttachments = /* @__PURE__ */ new Map();
17959
+ historyPageCache = /* @__PURE__ */ new Map();
17960
+ historyPageHits = 0;
17961
+ historyPageMisses = 0;
17962
+ historyCacheInspector;
18059
17963
  modelDirectory;
18060
17964
  modelDirectoryPromise;
18061
17965
  lastProjectionSeq = 0;
@@ -18171,6 +18075,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
18171
18075
  return business(await this.archiveSession(requestArg(args), signal));
18172
18076
  case "session/list":
18173
18077
  return business(success({ items: await this.sessionSummaries(signal) }));
18078
+ case "thread/list": {
18079
+ const catalog = await this.refreshCatalog(signal);
18080
+ return business(success({ data: catalog.threads, nextCursor: null }));
18081
+ }
18174
18082
  case "session/search":
18175
18083
  return business(await this.searchSessions(requestArg(args), signal));
18176
18084
  case "session/create":
@@ -18284,6 +18192,17 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
18284
18192
  this.selectedModels.clear();
18285
18193
  this.selectedPermissions.clear();
18286
18194
  this.imageAttachments.clear();
18195
+ this.historyPageCache.clear();
18196
+ const globalScope = globalThis;
18197
+ if (this.historyCacheInspector !== void 0 && globalScope.__dshCodexHistoryCacheStats === this.historyCacheInspector) {
18198
+ delete globalScope.__dshCodexHistoryCacheStats;
18199
+ }
18200
+ const scope = globalThis;
18201
+ if (scope.window !== void 0 && this.historyCacheInspector !== void 0 && scope.window.__dshCodexHistoryCacheStats === this.historyCacheInspector) {
18202
+ delete scope.window.__dshCodexHistoryCacheStats;
18203
+ }
18204
+ scope.document?.documentElement?.removeAttribute?.("data-dsh-codex-history-cache-stats");
18205
+ this.historyCacheInspector = void 0;
18287
18206
  }
18288
18207
  async refreshCatalog(signal) {
18289
18208
  const catalog = await loadCatalog(this.client, signal, this.pendingThreads);
@@ -18557,6 +18476,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
18557
18476
  });
18558
18477
  }
18559
18478
  acceptCodexFrame(follow, frame) {
18479
+ this.invalidateHistoryPages(follow.threadId);
18560
18480
  const params = record3(frame.params);
18561
18481
  if (frame.method === "thread/name/updated") {
18562
18482
  const name2 = string2(params.threadName);
@@ -19209,29 +19129,27 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
19209
19129
  return thread;
19210
19130
  }
19211
19131
  async readHistoryPage(threadId, page, signal) {
19212
- const maxMessages = page.maxMessages === void 0 ? void 0 : Math.min(CODEX_HISTORY_MAX_MESSAGES, Math.max(1, page.maxMessages));
19213
- let value;
19214
- try {
19215
- value = record3(await this.client.request("dsh/sessionHistory", {
19216
- threadId,
19217
- ...page.beforeSeq === void 0 ? {} : { beforeSeq: page.beforeSeq },
19218
- ...page.throughSeq === void 0 ? {} : { throughSeq: page.throughSeq },
19219
- ...maxMessages === void 0 ? {} : { maxMessages }
19220
- }, signal));
19221
- } catch (error) {
19222
- if (!isLegacySessionHistoryUnsupported(error)) throw error;
19223
- const result = record3(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
19224
- const thread = record3(result.thread);
19225
- if (string2(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
19226
- value = paginateCodexNativeHistory(
19227
- projectCodexNativeHistory(thread, `codex:${threadId}`, this.sessionGeneration),
19228
- {
19229
- beforeSeq: page.beforeSeq,
19230
- throughSeq: page.throughSeq,
19231
- maxMessages
19232
- }
19233
- );
19234
- }
19132
+ const maxMessages = Math.min(
19133
+ HISTORY_PAGE_SIZES[0],
19134
+ Math.max(1, page.maxMessages ?? HISTORY_PAGE_SIZES[0])
19135
+ );
19136
+ const cacheKey = `${this.host.deviceId}|${threadId}|${page.beforeSeq ?? ""}|${page.throughSeq ?? ""}|${maxMessages}`;
19137
+ const cached = this.historyPageCache.get(cacheKey);
19138
+ if (cached !== void 0 && Date.now() - cached.at < HISTORY_PAGE_CACHE_TTL_MS) {
19139
+ const ageMs = Date.now() - cached.at;
19140
+ this.historyPageHits += 1;
19141
+ this.cacheHistoryImages(cached.page);
19142
+ this.publishHistoryCacheStats();
19143
+ this.logHistoryCache("hit", threadId, { ...page, maxMessages }, cached.page.records.length, ageMs);
19144
+ return cached.page;
19145
+ }
19146
+ this.historyPageMisses += 1;
19147
+ let value = record3(await this.client.request("dsh/sessionHistory", {
19148
+ threadId,
19149
+ ...page.beforeSeq === void 0 ? {} : { beforeSeq: page.beforeSeq },
19150
+ ...page.throughSeq === void 0 ? {} : { throughSeq: page.throughSeq },
19151
+ maxMessages
19152
+ }, signal));
19235
19153
  value = adaptCodexHistoryPage(value, this.sessionGeneration);
19236
19154
  if (!isCodexNativeHistoryPage(value, `codex:${threadId}`)) {
19237
19155
  throw new Error("CodeX Remote returned an invalid paginated History.");
@@ -19241,8 +19159,115 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
19241
19159
  this.setPermissionSelection(`codex:${threadId}`, preset === "workspace-write" || preset === "danger-full-access" ? preset : void 0);
19242
19160
  }
19243
19161
  this.cacheHistoryImages(value);
19162
+ this.historyPageCache.set(cacheKey, { page: value, at: Date.now() });
19163
+ if (this.historyPageCache.size > HISTORY_PAGE_CACHE_LIMIT) {
19164
+ const oldest = this.historyPageCache.keys().next().value;
19165
+ if (oldest !== void 0) this.historyPageCache.delete(oldest);
19166
+ }
19167
+ this.publishHistoryCacheStats();
19168
+ this.logHistoryCache("miss", threadId, { ...page, maxMessages }, value.records.length);
19244
19169
  return value;
19245
19170
  }
19171
+ /** A live change or a new turn makes the cached page stale. */
19172
+ invalidateHistoryPages(threadId) {
19173
+ const marker = `|${threadId}|`;
19174
+ let removed = false;
19175
+ for (const key of [...this.historyPageCache.keys()]) {
19176
+ if (key.includes(marker)) {
19177
+ this.historyPageCache.delete(key);
19178
+ removed = true;
19179
+ }
19180
+ }
19181
+ if (removed) this.publishHistoryCacheStats();
19182
+ }
19183
+ /**
19184
+ * Expose cache metadata to the current page's DevTools as
19185
+ * `__dshCodexHistoryCacheStats()`. The function is metadata only: it never
19186
+ * returns message text or event data, so the Session content stays private.
19187
+ * It is installed on both the realm global and `window`, and mirrored into a
19188
+ * DOM data attribute so a console in another realm can read it too.
19189
+ */
19190
+ installHistoryCacheInspector() {
19191
+ const globalScope = globalThis;
19192
+ const scope = globalThis;
19193
+ const inspector = () => this.historyCacheStats();
19194
+ globalScope.__dshCodexHistoryCacheStats = inspector;
19195
+ if (scope.window !== void 0) scope.window.__dshCodexHistoryCacheStats = inspector;
19196
+ this.historyCacheInspector = inspector;
19197
+ this.publishHistoryCacheStats();
19198
+ }
19199
+ publishHistoryCacheStats() {
19200
+ const stats = this.historyCacheStats();
19201
+ this.writeHistoryCacheStatsFile(stats);
19202
+ const scope = globalThis;
19203
+ const root = scope.document?.documentElement;
19204
+ if (root?.setAttribute === void 0) return;
19205
+ try {
19206
+ root.setAttribute("data-dsh-codex-history-cache-stats", JSON.stringify(stats));
19207
+ } catch {
19208
+ }
19209
+ }
19210
+ /**
19211
+ * The Codex virtual harness runs in the DSH client's Node process, not in the
19212
+ * browser page, so DevTools cannot see its globals. Mirror the same metadata to
19213
+ * the client home for inspection without any message content.
19214
+ */
19215
+ writeHistoryCacheStatsFile(stats) {
19216
+ if (!codexHistoryStatsFileEnabled() || process.env.VITEST !== void 0) return;
19217
+ try {
19218
+ const home = process.env.DSH_HOME ?? join4(homedir(), ".dsh");
19219
+ mkdirSync(home, { recursive: true });
19220
+ writeFileSync(
19221
+ join4(home, "codex-history-cache-stats.json"),
19222
+ JSON.stringify({
19223
+ ...stats,
19224
+ pid: process.pid,
19225
+ source: "codex-harness",
19226
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString()
19227
+ }, null, 2)
19228
+ );
19229
+ } catch {
19230
+ }
19231
+ }
19232
+ logHistoryCache(event, threadId, page, records, ageMs) {
19233
+ if (!codexHistoryStatsFileEnabled()) return;
19234
+ const scope = globalThis;
19235
+ scope.console?.info?.("[dsh-remote] codex history page cache " + event, {
19236
+ threadId,
19237
+ beforeSeq: page.beforeSeq ?? null,
19238
+ ...page.maxMessages === void 0 ? {} : { maxMessages: page.maxMessages },
19239
+ ...records === void 0 ? {} : { records },
19240
+ ...ageMs === void 0 ? {} : { ageMs },
19241
+ hits: this.historyPageHits,
19242
+ misses: this.historyPageMisses
19243
+ });
19244
+ }
19245
+ historyCacheStats() {
19246
+ const now = Date.now();
19247
+ return {
19248
+ ttlMs: HISTORY_PAGE_CACHE_TTL_MS,
19249
+ hits: this.historyPageHits,
19250
+ misses: this.historyPageMisses,
19251
+ active: true,
19252
+ entries: [...this.historyPageCache.entries()].map(([key, { page, at }]) => {
19253
+ const records = page.records;
19254
+ const firstSeq = records[0]?.event.seq;
19255
+ const lastSeq = records.at(-1)?.event.seq;
19256
+ return {
19257
+ key,
19258
+ ageMs: now - at,
19259
+ expired: now - at >= HISTORY_PAGE_CACHE_TTL_MS,
19260
+ sessionId: page.header.id,
19261
+ records: records.length,
19262
+ cursor: page.cursor,
19263
+ nextTurn: page.nextTurn,
19264
+ hasMore: page.hasMore,
19265
+ ...firstSeq === void 0 ? {} : { firstSeq },
19266
+ ...lastSeq === void 0 ? {} : { lastSeq }
19267
+ };
19268
+ })
19269
+ };
19270
+ }
19246
19271
  cacheHistoryImages(history) {
19247
19272
  const sessionId = history.header.id;
19248
19273
  const entries = "entries" in history ? history.entries : history.records;
@@ -19504,12 +19529,26 @@ async function loadCatalog(client, signal, pendingThreads) {
19504
19529
  const workspaceByProjectId = new Map(projects.map((project, index) => [project.id, projectWorkspaces[index]]));
19505
19530
  const visibleSessionIds = /* @__PURE__ */ new Set();
19506
19531
  const visibleThreads = [];
19532
+ const unmatched = [];
19507
19533
  for (const { thread, session } of projected) {
19508
19534
  const projectWorkspaceForSession = findProjectWorkspace(thread, session, projects, workspaceByProjectId);
19509
19535
  if (projectWorkspaceForSession !== void 0) {
19536
+ rebaseSessionCwdOnProjectRoot(session, projectWorkspaceForSession.path);
19537
+ rebaseThreadCwdOnProjectRoot(thread, projectWorkspaceForSession.path);
19510
19538
  addSessionToWorkspace(projectWorkspaceForSession, session);
19511
19539
  visibleSessionIds.add(session.id);
19512
19540
  visibleThreads.push(thread);
19541
+ } else {
19542
+ unmatched.push({ thread, session });
19543
+ }
19544
+ }
19545
+ if (unmatched.length > 0) {
19546
+ const workspace = defaultCodexWorkspace(unmatched.map((value) => value.session));
19547
+ projectWorkspaces.push(workspace);
19548
+ for (const { thread, session } of unmatched) {
19549
+ addSessionToWorkspace(workspace, session);
19550
+ visibleSessionIds.add(session.id);
19551
+ visibleThreads.push(thread);
19513
19552
  }
19514
19553
  }
19515
19554
  const sessions = projected.map((value) => value.session).filter((session) => visibleSessionIds.has(session.id));
@@ -19600,13 +19639,17 @@ async function loadModelDirectory(client, signal) {
19600
19639
  models
19601
19640
  };
19602
19641
  }
19603
- function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legacy") {
19642
+ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legacy", start = {}) {
19604
19643
  const entries = [];
19605
- let seq = 0;
19606
- let turnNumber = 0;
19644
+ let seq = start.seq ?? 0;
19645
+ let turnNumber = start.turn ?? 0;
19646
+ let turnSeqBase = 0;
19647
+ let turnEventIndex = 0;
19648
+ let lastSeq = start.stable === true ? -1 : (start.seq ?? 0) - 1;
19607
19649
  const active = activeTurnId(thread.turns);
19608
19650
  const append = (type, data2, time = Date.now(), view, sourceEventSeqs) => {
19609
- const eventSeq = seq++;
19651
+ const eventSeq = start.stable === true ? turnSeqBase + turnEventIndex++ : seq++;
19652
+ lastSeq = eventSeq;
19610
19653
  entries.push({
19611
19654
  type: "event",
19612
19655
  event: {
@@ -19623,13 +19666,16 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
19623
19666
  };
19624
19667
  for (const rawTurn of array(thread.turns)) {
19625
19668
  const turn = record3(rawTurn);
19669
+ const turnId = string2(turn.id);
19626
19670
  const turnItems = array(turn.items).map(record3);
19627
19671
  const toolImages = turnItems.flatMap(toolOutputImageBlocks);
19628
19672
  let imageOwner = -1;
19629
19673
  for (let index = 0; index < turnItems.length; index += 1) {
19630
19674
  if (turnItems[index].type === "agentMessage") imageOwner = index;
19631
19675
  }
19632
- turnNumber += 1;
19676
+ turnNumber = (turnId === void 0 ? void 0 : start.turnNumbers?.get(turnId)) ?? turnNumber + 1;
19677
+ turnSeqBase = turnNumber * 4096;
19678
+ turnEventIndex = 0;
19633
19679
  const turnActive = isActiveCodexTurn(turn);
19634
19680
  const time = normalizeTime(turn.createdAt) || normalizeTime(turn.startedAt) || normalizeTime(thread.createdAt) || Date.now();
19635
19681
  append("turn/start", { turn: turnNumber }, time);
@@ -19673,7 +19719,7 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
19673
19719
  ...sessionGeneration === "v3" ? { isSeeded: false } : {}
19674
19720
  },
19675
19721
  entries,
19676
- lastSeq: seq - 1,
19722
+ lastSeq,
19677
19723
  nextTurn: turnNumber,
19678
19724
  ...active === void 0 ? {} : { activeTurnId: active }
19679
19725
  };
@@ -19682,7 +19728,7 @@ function paginateCodexNativeHistory(history, request) {
19682
19728
  const throughSeq = Math.min(request.throughSeq ?? history.lastSeq, history.lastSeq);
19683
19729
  const endSeq = Math.min(throughSeq, request.beforeSeq === void 0 ? throughSeq : request.beforeSeq - 1);
19684
19730
  const window = endSeq < 0 ? [] : history.entries.filter((entry) => entry.event.seq <= endSeq);
19685
- const maxMessages = request.maxMessages ?? DEFAULT_HISTORY_MESSAGES;
19731
+ const maxMessages = request.maxMessages ?? HISTORY_PAGE_SIZES[0];
19686
19732
  let messages = 0;
19687
19733
  let cut = 0;
19688
19734
  for (let index = window.length - 1; index >= 0; index -= 1) {
@@ -19957,6 +20003,20 @@ function nativeWorkspace(view) {
19957
20003
  function nativeVisibleWorkspaces(catalog, selectedWorkspaceId) {
19958
20004
  return catalog.workspaces.filter((workspace) => workspace.sessionIds.length > 0 || workspace.workspaceId === selectedWorkspaceId);
19959
20005
  }
20006
+ var DEFAULT_CODEX_WORKSPACE_ID = "codex-workspace:default";
20007
+ var DEFAULT_CODEX_WORKSPACE_TITLE = "\u9ED8\u8BA4\u5DE5\u4F5C\u533A";
20008
+ function defaultCodexWorkspace(sessions) {
20009
+ const first = sessions[0];
20010
+ return {
20011
+ workspaceId: DEFAULT_CODEX_WORKSPACE_ID,
20012
+ path: sessions.find((session) => session.cwd !== void 0)?.cwd ?? "/",
20013
+ title: DEFAULT_CODEX_WORKSPACE_TITLE,
20014
+ sessionIds: [],
20015
+ sessionCount: 0,
20016
+ createdAt: isoTime(first.createdAt),
20017
+ updatedAt: isoTime(first.updatedAt || first.createdAt)
20018
+ };
20019
+ }
19960
20020
  function projectWorkspace(project) {
19961
20021
  const root = project.roots[0];
19962
20022
  return {
@@ -19998,6 +20058,28 @@ function findProjectWorkspace(thread, session, projects, workspaceByProjectId) {
19998
20058
  })[0];
19999
20059
  return match === void 0 ? void 0 : workspaceByProjectId.get(match.project.id);
20000
20060
  }
20061
+ function rebasedCwdOnProjectRoot(cwd, projectRoot2) {
20062
+ if (process.platform !== "win32") return void 0;
20063
+ const root = projectRoot2.replace(/[\\/]+$/u, "");
20064
+ if (root.length === 0) return void 0;
20065
+ const normalizedRoot = root.toLowerCase();
20066
+ const normalizedCwd = cwd.toLowerCase();
20067
+ if (normalizedCwd === normalizedRoot) return root;
20068
+ if (normalizedCwd.startsWith(`${normalizedRoot}\\`) || normalizedCwd.startsWith(`${normalizedRoot}/`)) {
20069
+ return `${root}${cwd.slice(root.length)}`;
20070
+ }
20071
+ return void 0;
20072
+ }
20073
+ function rebaseSessionCwdOnProjectRoot(session, projectRoot2) {
20074
+ if (session.cwd === void 0) return;
20075
+ const next = rebasedCwdOnProjectRoot(session.cwd, projectRoot2);
20076
+ if (next !== void 0) session.cwd = next;
20077
+ }
20078
+ function rebaseThreadCwdOnProjectRoot(thread, projectRoot2) {
20079
+ if (typeof thread.cwd !== "string") return;
20080
+ const next = rebasedCwdOnProjectRoot(thread.cwd, projectRoot2);
20081
+ if (next !== void 0) thread.cwd = next;
20082
+ }
20001
20083
  function addSessionToWorkspace(workspace, session) {
20002
20084
  if (!workspace.sessionIds.includes(session.id)) {
20003
20085
  workspace.sessionIds.push(session.id);
@@ -20014,7 +20096,8 @@ function containsPath(root, candidate) {
20014
20096
  return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}/`) || normalizedCandidate.startsWith(`${normalizedRoot}\\`);
20015
20097
  }
20016
20098
  function normalizePathForCompare(path) {
20017
- return path.replace(/[\\/]+$/u, "") || path;
20099
+ const trimmed = path.replace(/[\\/]+$/u, "") || path;
20100
+ return process.platform === "win32" ? trimmed.toLowerCase() : trimmed;
20018
20101
  }
20019
20102
  function modelSelection() {
20020
20103
  return { provider: CODEX_PROVIDER, model: CODEX_MODEL };
@@ -20264,10 +20347,6 @@ function failFrom(error) {
20264
20347
  const source = error instanceof Error ? error : new Error(String(error));
20265
20348
  return fail(errorCode3(source) ?? "internal", source.message, errorDetails(source));
20266
20349
  }
20267
- function isLegacySessionHistoryUnsupported(error) {
20268
- if (!(error instanceof Error)) return false;
20269
- return errorCode3(error) === "METHOD_NOT_ALLOWED" || errorCode3(error) === "METHOD_NOT_FOUND" || error.message.includes("The requested Codex method is not available over Remote.");
20270
- }
20271
20350
  function errorCode3(error) {
20272
20351
  return "code" in error && typeof error.code === "string" ? error.code : void 0;
20273
20352
  }
@@ -20388,7 +20467,7 @@ var AsyncValueQueue2 = class {
20388
20467
  continue;
20389
20468
  }
20390
20469
  if (this.closed) return;
20391
- const next = await new Promise((resolve4) => this.waiters.push(resolve4));
20470
+ const next = await new Promise((resolve5) => this.waiters.push(resolve5));
20392
20471
  if (next.done) return;
20393
20472
  yield next.value;
20394
20473
  }
@@ -20410,7 +20489,13 @@ function normalizeByteResult2(endpoint, result) {
20410
20489
 
20411
20490
  // src/typert-gateway-switch.ts
20412
20491
  var REMOTE_COMMAND_METHODS = ["execute", "list"];
20413
- var LOCAL_ONLY_NAMESPACES = /* @__PURE__ */ new Set(["dynamicCordisRunner"]);
20492
+ var LOCAL_ONLY_NAMESPACES = /* @__PURE__ */ new Set([
20493
+ "account",
20494
+ "dynamicCordisRunner",
20495
+ "remoteAccess",
20496
+ "schedule",
20497
+ "web"
20498
+ ]);
20414
20499
  var LOCAL_FALLBACK_NAMESPACES = /* @__PURE__ */ new Set(["$events", "settings", "credentials", "dynamicCordisRunner"]);
20415
20500
  var PEER_GONE_CODES = /* @__PURE__ */ new Set([
20416
20501
  "TRANSPORT_CLOSED",
@@ -20767,7 +20852,7 @@ import { networkInterfaces } from "node:os";
20767
20852
  // src/native-rtc-helper.ts
20768
20853
  import { spawn, spawnSync } from "node:child_process";
20769
20854
  import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
20770
- import { delimiter, dirname as dirname3, join as join4 } from "node:path";
20855
+ import { delimiter, dirname as dirname3, join as join5 } from "node:path";
20771
20856
  import { fileURLToPath } from "node:url";
20772
20857
  var cachedExternalFactory;
20773
20858
  var cachedExternalFactoryResolved = false;
@@ -20818,21 +20903,21 @@ function nodeBinaryCandidates() {
20818
20903
  add3(process.env.DSH_REMOTE_NODE);
20819
20904
  add3(process.env.NODE);
20820
20905
  if (process.versions.electron === void 0) add3(process.execPath);
20821
- for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join4(part, process.platform === "win32" ? "node.exe" : "node"));
20906
+ for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join5(part, process.platform === "win32" ? "node.exe" : "node"));
20822
20907
  add3("/opt/homebrew/bin/node");
20823
20908
  add3("/usr/local/bin/node");
20824
20909
  add3("/usr/bin/node");
20825
- add3(join4(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
20826
- add3(join4(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
20827
- add3(join4(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
20910
+ add3(join5(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
20911
+ add3(join5(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
20912
+ add3(join5(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
20828
20913
  for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
20829
20914
  return candidates;
20830
20915
  }
20831
20916
  function nvmNodeCandidates() {
20832
- const root = join4(process.env.HOME ?? "", ".nvm", "versions", "node");
20917
+ const root = join5(process.env.HOME ?? "", ".nvm", "versions", "node");
20833
20918
  if (root === "" || !existsSync(root)) return [];
20834
20919
  try {
20835
- return readdirSync(root).map((version) => join4(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
20920
+ return readdirSync(root).map((version) => join5(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
20836
20921
  } catch {
20837
20922
  return [];
20838
20923
  }
@@ -20955,8 +21040,8 @@ var ExternalNativePeerConnection = class {
20955
21040
  request(method, payload) {
20956
21041
  if (this.closed) return Promise.reject(new Error("native rtc helper is closed"));
20957
21042
  const id4 = this.nextRequestId++;
20958
- const promise = new Promise((resolve4, reject) => {
20959
- this.pending.set(id4, { resolve: resolve4, reject });
21043
+ const promise = new Promise((resolve5, reject) => {
21044
+ this.pending.set(id4, { resolve: resolve5, reject });
20960
21045
  });
20961
21046
  this.write({ id: id4, method, payload });
20962
21047
  return promise;
@@ -21750,10 +21835,10 @@ function parseRouteTarget(value) {
21750
21835
  async function detectRouteHostIpv4(target2, timeoutMs) {
21751
21836
  const socket = createSocket("udp4");
21752
21837
  try {
21753
- return await new Promise((resolve4, reject) => {
21838
+ return await new Promise((resolve5, reject) => {
21754
21839
  const timer = setTimeout(() => {
21755
21840
  cleanup();
21756
- resolve4(void 0);
21841
+ resolve5(void 0);
21757
21842
  }, timeoutMs);
21758
21843
  const cleanup = () => {
21759
21844
  clearTimeout(timer);
@@ -21767,7 +21852,7 @@ async function detectRouteHostIpv4(target2, timeoutMs) {
21767
21852
  socket.connect(target2.port, target2.host, () => {
21768
21853
  cleanup();
21769
21854
  const address = socket.address();
21770
- resolve4(typeof address === "string" ? void 0 : address.address);
21855
+ resolve5(typeof address === "string" ? void 0 : address.address);
21771
21856
  });
21772
21857
  });
21773
21858
  } finally {
@@ -21862,6 +21947,7 @@ var ClientModeRuntime = class {
21862
21947
  this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
21863
21948
  this.gatewaySwitch = new TypertGatewaySwitch(typertGateway, { peer: this.peer });
21864
21949
  this.gatewaySwitch.setRemoteAvailability(() => this.connected !== void 0);
21950
+ this.installCodexHistoryCacheInspector();
21865
21951
  }
21866
21952
  preview;
21867
21953
  identity;
@@ -23316,6 +23402,43 @@ var ClientModeRuntime = class {
23316
23402
  const virtual = this.codexVirtual;
23317
23403
  this.codexVirtual = void 0;
23318
23404
  await virtual?.close();
23405
+ this.installCodexHistoryCacheInspector();
23406
+ }
23407
+ /**
23408
+ * Keep `__dshCodexHistoryCacheStats()` defined for the whole client lifetime.
23409
+ * The Codex virtual harness has its own inspector while a workspace is open;
23410
+ * this runtime-level one fills the gap before the first open and after close.
23411
+ */
23412
+ installCodexHistoryCacheInspector() {
23413
+ const globalScope = globalThis;
23414
+ const scope = globalThis;
23415
+ const inspect = () => this.codexVirtual?.historyCacheStats() ?? {
23416
+ ttlMs: 6e4,
23417
+ hits: 0,
23418
+ misses: 0,
23419
+ active: false,
23420
+ entries: []
23421
+ };
23422
+ globalScope.__dshCodexHistoryCacheStats = inspect;
23423
+ if (scope.window !== void 0) scope.window.__dshCodexHistoryCacheStats = inspect;
23424
+ this.writeCodexHistoryCacheStats(inspect());
23425
+ }
23426
+ writeCodexHistoryCacheStats(stats) {
23427
+ if (!codexHistoryStatsFileEnabled() || process.env.VITEST !== void 0) return;
23428
+ try {
23429
+ const home = process.env.DSH_HOME ?? join6(homedir2(), ".dsh");
23430
+ mkdirSync2(home, { recursive: true });
23431
+ writeFileSync2(
23432
+ join6(home, "codex-history-cache-stats.json"),
23433
+ JSON.stringify({
23434
+ ...stats,
23435
+ pid: process.pid,
23436
+ source: "client-runtime",
23437
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString()
23438
+ }, null, 2)
23439
+ );
23440
+ } catch {
23441
+ }
23319
23442
  }
23320
23443
  assertRemoteCompatible(remote) {
23321
23444
  this.selectHarnessRemoteTransport(remote);
@@ -23912,8 +24035,8 @@ function failureCode(error) {
23912
24035
  return own ?? safeErrorCode(error);
23913
24036
  }
23914
24037
  function sleep3(ms) {
23915
- return new Promise((resolve4) => {
23916
- setTimeout(resolve4, ms);
24038
+ return new Promise((resolve5) => {
24039
+ setTimeout(resolve5, ms);
23917
24040
  });
23918
24041
  }
23919
24042
  function remoteHostFeatures(clientVersion) {
@@ -23964,7 +24087,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
23964
24087
  }
23965
24088
  async function waitForCodexFrames(stream, signal) {
23966
24089
  if (signal?.aborted) throw new ClientModeError("RPC_ABORTED", "The Codex event poll was cancelled.");
23967
- await new Promise((resolve4, reject) => {
24090
+ await new Promise((resolve5, reject) => {
23968
24091
  const previousWake = stream.wake;
23969
24092
  const timer = setTimeout(done, 25e3);
23970
24093
  const onAbort = () => {
@@ -23978,7 +24101,7 @@ async function waitForCodexFrames(stream, signal) {
23978
24101
  }
23979
24102
  function done() {
23980
24103
  cleanup();
23981
- resolve4();
24104
+ resolve5();
23982
24105
  }
23983
24106
  stream.wake = () => {
23984
24107
  previousWake();
@@ -24023,14 +24146,14 @@ function iceServersForAttempt(attempt, iceServers) {
24023
24146
  // src/client-target-store.ts
24024
24147
  import { randomUUID as randomUUID2 } from "node:crypto";
24025
24148
  import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
24026
- import { join as join5 } from "node:path";
24149
+ import { join as join7 } from "node:path";
24027
24150
  var FILE_NAME = "client-target.json";
24028
24151
  var ClientTargetStore = class {
24029
24152
  constructor(directory) {
24030
24153
  this.directory = directory;
24031
24154
  }
24032
24155
  get file() {
24033
- return join5(this.directory, FILE_NAME);
24156
+ return join7(this.directory, FILE_NAME);
24034
24157
  }
24035
24158
  /** Read the record. A missing, unreadable or malformed file reads as "no record". */
24036
24159
  async load() {
@@ -24106,8 +24229,8 @@ import { execFileSync } from "node:child_process";
24106
24229
  import { existsSync as existsSync2 } from "node:fs";
24107
24230
  import { createHash } from "node:crypto";
24108
24231
  import { chmod as chmod3, cp, mkdir as mkdir3, readFile as readFile4, rm as rm3, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
24109
- import { homedir } from "node:os";
24110
- import { dirname as dirname4, join as join6 } from "node:path";
24232
+ import { homedir as homedir3 } from "node:os";
24233
+ import { dirname as dirname4, join as join8 } from "node:path";
24111
24234
  var identitySchema = external_exports.object({
24112
24235
  schemaVersion: external_exports.literal(1),
24113
24236
  deviceId: external_exports.string().uuid(),
@@ -24136,14 +24259,14 @@ var IdentityStore = class {
24136
24259
  peers = /* @__PURE__ */ new Map();
24137
24260
  constructor(options = {}) {
24138
24261
  const env = options.env ?? process.env;
24139
- const dshHome = env.DSH_HOME || join6(options.homeDirectory ?? homedir(), ".dsh");
24140
- this.directory = options.directory ?? join6(dshHome, "remote");
24262
+ const dshHome = env.DSH_HOME || join8(options.homeDirectory ?? homedir3(), ".dsh");
24263
+ this.directory = options.directory ?? join8(dshHome, "remote");
24141
24264
  }
24142
24265
  async loadOrCreate(deviceName) {
24143
24266
  await mkdir3(this.directory, { recursive: true, mode: 448 });
24144
24267
  await chmod3(this.directory, 448);
24145
- const devicePath = join6(this.directory, "device.json");
24146
- const keyPath = join6(this.directory, "device.key");
24268
+ const devicePath = join8(this.directory, "device.json");
24269
+ const keyPath = join8(this.directory, "device.key");
24147
24270
  const [hasDevice, hasKey] = await Promise.all([exists2(devicePath), exists2(keyPath)]);
24148
24271
  if (hasDevice !== hasKey) {
24149
24272
  throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
@@ -24212,7 +24335,7 @@ var IdentityStore = class {
24212
24335
  return removed;
24213
24336
  }
24214
24337
  async loadPeers() {
24215
- const path = join6(this.directory, "trusted-peers.json");
24338
+ const path = join8(this.directory, "trusted-peers.json");
24216
24339
  if (!await exists2(path)) {
24217
24340
  await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
24218
24341
  }
@@ -24228,7 +24351,7 @@ var IdentityStore = class {
24228
24351
  this.peers = peers;
24229
24352
  }
24230
24353
  async savePeers() {
24231
- await atomicJsonWrite(join6(this.directory, "trusted-peers.json"), {
24354
+ await atomicJsonWrite(join8(this.directory, "trusted-peers.json"), {
24232
24355
  schemaVersion: 1,
24233
24356
  peers: [...this.peers.values()]
24234
24357
  }, 384);
@@ -24237,7 +24360,7 @@ var IdentityStore = class {
24237
24360
  function serverStorageDirectory(root, serverUrl, role) {
24238
24361
  const origin = new URL(serverUrl).origin;
24239
24362
  const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
24240
- return join6(root, "servers", scope, role);
24363
+ return join8(root, "servers", scope, role);
24241
24364
  }
24242
24365
  async function ensureDeviceDirectory(root, serverUrl) {
24243
24366
  const directory = serverStorageDirectory(root, serverUrl, "device");
@@ -24438,10 +24561,11 @@ var ControlStatusStream = class {
24438
24561
 
24439
24562
  // src/codex/domain.ts
24440
24563
  import { randomUUID as randomUUID3 } from "node:crypto";
24564
+ import { Buffer as Buffer4 } from "node:buffer";
24441
24565
  import { accessSync, constants, existsSync as existsSync3, readFileSync, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
24442
24566
  import { readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
24443
- import { homedir as homedir2 } from "node:os";
24444
- import { basename as basename2, isAbsolute as isAbsolute2, join as join7, posix, relative, resolve } from "node:path";
24567
+ import { homedir as homedir4 } from "node:os";
24568
+ import { basename as basename2, isAbsolute as isAbsolute2, join as join9, posix, relative, resolve } from "node:path";
24445
24569
 
24446
24570
  // src/codex/app-server.ts
24447
24571
  import { spawn as spawn2 } from "node:child_process";
@@ -24514,15 +24638,15 @@ var CodexAppServerClient = class {
24514
24638
  const child = this.process;
24515
24639
  this.process = void 0;
24516
24640
  if (child === void 0 || child.exitCode !== null || child.killed) return;
24517
- await new Promise((resolve4) => {
24641
+ await new Promise((resolve5) => {
24518
24642
  const timer = setTimeout(() => {
24519
24643
  child.kill("SIGKILL");
24520
- resolve4();
24644
+ resolve5();
24521
24645
  }, 2e3);
24522
24646
  timer.unref?.();
24523
24647
  child.once("exit", () => {
24524
24648
  clearTimeout(timer);
24525
- resolve4();
24649
+ resolve5();
24526
24650
  });
24527
24651
  child.kill("SIGTERM");
24528
24652
  });
@@ -24578,13 +24702,13 @@ var CodexAppServerClient = class {
24578
24702
  }
24579
24703
  request(method, params, timeoutMs) {
24580
24704
  const id4 = this.nextId++;
24581
- const result = new Promise((resolve4, reject) => {
24705
+ const result = new Promise((resolve5, reject) => {
24582
24706
  const timer = setTimeout(() => {
24583
24707
  this.pending.delete(id4);
24584
24708
  reject(new CodexAppServerError("CODEX_REQUEST_TIMEOUT", "Codex App Server request timed out."));
24585
24709
  }, timeoutMs);
24586
24710
  timer.unref?.();
24587
- this.pending.set(id4, { resolve: resolve4, reject, timer });
24711
+ this.pending.set(id4, { resolve: resolve5, reject, timer });
24588
24712
  });
24589
24713
  try {
24590
24714
  this.write({ id: id4, method, params });
@@ -24698,20 +24822,149 @@ function isRecord10(value) {
24698
24822
  return typeof value === "object" && value !== null && !Array.isArray(value);
24699
24823
  }
24700
24824
 
24701
- // src/codex/peer-bridge.ts
24702
- import { Buffer as Buffer3 } from "node:buffer";
24703
- var streamOpenSchema = external_exports.object({
24704
- streamId: external_exports.string().min(1).max(128),
24705
- threadId: external_exports.string().min(1).max(256)
24706
- }).strict();
24707
- var streamCloseSchema = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
24708
- var transferOpenSchema = external_exports.object({
24709
- transferId: external_exports.string().uuid(),
24710
- totalBytes: external_exports.number().int().positive().max(MAX_CODEX_APP_TRANSFER_BYTES),
24711
- totalChunks: external_exports.number().int().positive()
24825
+ // src/codex/method-policy.ts
24826
+ var id2 = external_exports.string().min(1).max(256);
24827
+ var cursor = external_exports.string().min(1).max(4096).nullable().optional();
24828
+ var textInput = external_exports.object({
24829
+ type: external_exports.literal("text"),
24830
+ text: external_exports.string().min(1).max(256 * 1024)
24712
24831
  }).strict();
24713
- var transferChunkSchema = external_exports.object({
24714
- transferId: external_exports.string().uuid(),
24832
+ var imageMediaType = external_exports.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]);
24833
+ var canonicalBase64 = external_exports.string().min(4).max(288 * 1024 * 1024).refine((value) => value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(value), {
24834
+ message: "Image data must use canonical base64."
24835
+ });
24836
+ var imageInput = external_exports.object({
24837
+ type: external_exports.literal("image"),
24838
+ mediaType: imageMediaType,
24839
+ data: canonicalBase64
24840
+ }).strict();
24841
+ var input = external_exports.array(external_exports.union([textInput, imageInput])).min(1).max(16);
24842
+ var permissionPreset = external_exports.enum(["workspace-write", "danger-full-access"]);
24843
+ var projectRoot = external_exports.object({
24844
+ path: external_exports.string().min(1).max(4096)
24845
+ }).strict();
24846
+ var CODEX_HISTORY_MAX_MESSAGES = 200;
24847
+ var schemas = {
24848
+ "account/read": external_exports.object({ refreshToken: external_exports.literal(false).optional() }).strict(),
24849
+ "model/list": external_exports.object({
24850
+ cursor,
24851
+ limit: external_exports.number().int().min(1).max(100).optional(),
24852
+ includeHidden: external_exports.boolean().optional()
24853
+ }).strict(),
24854
+ "project/list": external_exports.object({
24855
+ cursor,
24856
+ limit: external_exports.number().int().min(1).max(100).optional()
24857
+ }).strict(),
24858
+ "project/create": external_exports.object({
24859
+ name: external_exports.string().trim().min(1).max(256),
24860
+ roots: external_exports.array(projectRoot).length(1),
24861
+ idempotencyKey: external_exports.string().min(16).max(256)
24862
+ }).strict(),
24863
+ "thread/list": external_exports.object({
24864
+ cursor,
24865
+ limit: external_exports.number().int().min(1).max(100).optional(),
24866
+ sortKey: external_exports.enum(["created_at", "updated_at", "recency_at"]).optional(),
24867
+ sortDirection: external_exports.enum(["asc", "desc"]).optional(),
24868
+ modelProviders: external_exports.array(external_exports.string().min(1).max(128)).max(32).nullable().optional(),
24869
+ sourceKinds: external_exports.array(external_exports.enum(["cli", "vscode", "exec", "appServer", "unknown"])).max(8).optional(),
24870
+ archived: external_exports.boolean().optional(),
24871
+ isPinned: external_exports.boolean().optional(),
24872
+ cwd: external_exports.union([external_exports.string().min(1).max(4096), external_exports.array(external_exports.string().min(1).max(4096)).min(1).max(32)]).optional(),
24873
+ useStateDbOnly: external_exports.boolean().optional(),
24874
+ searchTerm: external_exports.string().max(1024).optional(),
24875
+ // Upstream `ThreadListParams` declares these two as well. Refusing them turned a
24876
+ // legitimate client query into "The CodeX call parameters are invalid.".
24877
+ originators: external_exports.array(external_exports.string().min(1).max(256)).max(32).nullable().optional(),
24878
+ sectionId: external_exports.string().max(256).nullable().optional()
24879
+ }).strict(),
24880
+ "thread/read": external_exports.object({ threadId: id2, includeTurns: external_exports.boolean().optional() }).strict(),
24881
+ "dsh/sessionHistory": external_exports.object({
24882
+ threadId: id2,
24883
+ beforeSeq: external_exports.number().int().nonnegative().optional(),
24884
+ throughSeq: external_exports.number().int().min(-1).optional(),
24885
+ maxMessages: external_exports.number().int().min(1).max(CODEX_HISTORY_MAX_MESSAGES).optional()
24886
+ }).strict(),
24887
+ "dsh/directoryList": external_exports.object({
24888
+ path: external_exports.string().min(1).max(4096)
24889
+ }).strict(),
24890
+ "thread/start": external_exports.object({
24891
+ cwd: external_exports.string().min(1).max(4096),
24892
+ model: external_exports.string().min(1).max(128).optional(),
24893
+ personality: external_exports.string().min(1).max(64).optional(),
24894
+ permissionPreset: permissionPreset.optional()
24895
+ }).strict(),
24896
+ "thread/resume": external_exports.object({
24897
+ threadId: id2,
24898
+ model: external_exports.string().min(1).max(128).optional(),
24899
+ permissionPreset: permissionPreset.optional()
24900
+ }).strict(),
24901
+ "thread/fork": external_exports.object({
24902
+ threadId: id2,
24903
+ lastTurnId: id2.optional(),
24904
+ permissionPreset: permissionPreset.optional()
24905
+ }).strict(),
24906
+ // Upstream declares a plain string and uses the empty value to clear a name, so the
24907
+ // trim-based minimum rejected a legitimate rename.
24908
+ "thread/name/set": external_exports.object({ threadId: id2, name: external_exports.string().max(256) }).strict(),
24909
+ "thread/archive": external_exports.object({ threadId: id2 }).strict(),
24910
+ "thread/unarchive": external_exports.object({ threadId: id2 }).strict(),
24911
+ "thread/unsubscribe": external_exports.object({ threadId: id2 }).strict(),
24912
+ "turn/start": external_exports.object({
24913
+ threadId: id2,
24914
+ input,
24915
+ model: external_exports.string().min(1).max(128).optional(),
24916
+ effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]).optional(),
24917
+ summary: external_exports.enum(["auto", "concise", "detailed", "none"]).optional(),
24918
+ personality: external_exports.string().min(1).max(64).optional(),
24919
+ permissionPreset: permissionPreset.optional()
24920
+ }).strict(),
24921
+ "turn/steer": external_exports.object({
24922
+ threadId: id2,
24923
+ input,
24924
+ expectedTurnId: id2,
24925
+ // Declared by upstream `TurnSteerParams`; see the thread/list note above.
24926
+ clientUserMessageId: external_exports.string().max(256).nullable().optional()
24927
+ }).strict(),
24928
+ "turn/interrupt": external_exports.object({ threadId: id2, turnId: id2 }).strict()
24929
+ };
24930
+ var CODEX_APP_ALLOWLIST = Object.freeze(Object.keys(schemas));
24931
+ function parseCodexCall(method, params) {
24932
+ if (!Object.prototype.hasOwnProperty.call(schemas, method)) {
24933
+ throw new RpcError("METHOD_NOT_ALLOWED", "The requested Codex method is not available over Remote.");
24934
+ }
24935
+ const schema = schemas[method];
24936
+ const parsed = schema.safeParse(params);
24937
+ if (!parsed.success) {
24938
+ const detail = parsed.error.issues.slice(0, 4).map((issue) => {
24939
+ const path = issue.path.length === 0 ? "(root)" : issue.path.join(".");
24940
+ const keys = "keys" in issue && Array.isArray(issue.keys) ? ` [${issue.keys.join(", ")}]` : "";
24941
+ return `${path}${keys}: ${issue.code}`;
24942
+ }).join(", ");
24943
+ throw new RpcError("INVALID_MESSAGE", `The CodeX call parameters are invalid. (${method} \u2192 ${detail})`);
24944
+ }
24945
+ return { method, params: parsed.data };
24946
+ }
24947
+ function isThreadMutation(method) {
24948
+ return method === "turn/start" || method === "turn/steer" || method === "turn/interrupt";
24949
+ }
24950
+ function threadIdFromParams(params) {
24951
+ return typeof params.threadId === "string" ? params.threadId : void 0;
24952
+ }
24953
+
24954
+ // src/codex/peer-bridge.ts
24955
+ import { Buffer as Buffer3 } from "node:buffer";
24956
+ var streamOpenSchema = external_exports.object({
24957
+ streamId: external_exports.string().min(1).max(128),
24958
+ threadId: external_exports.string().min(1).max(256)
24959
+ }).strict();
24960
+ var streamCloseSchema = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
24961
+ var transferOpenSchema = external_exports.object({
24962
+ transferId: external_exports.string().uuid(),
24963
+ totalBytes: external_exports.number().int().positive().max(MAX_CODEX_APP_TRANSFER_BYTES),
24964
+ totalChunks: external_exports.number().int().positive()
24965
+ }).strict();
24966
+ var transferChunkSchema = external_exports.object({
24967
+ transferId: external_exports.string().uuid(),
24715
24968
  index: external_exports.number().int().nonnegative(),
24716
24969
  data: external_exports.string().min(1).max(Math.ceil(CODEX_APP_TRANSFER_CHUNK_BYTES / 3) * 4)
24717
24970
  }).strict();
@@ -24988,6 +25241,219 @@ function concatChunks(chunks, totalBytes) {
24988
25241
  return output;
24989
25242
  }
24990
25243
 
25244
+ // src/codex/history-window.ts
25245
+ var PAGE_LIMIT = 25;
25246
+ var MAX_PAGES = 64;
25247
+ var CACHE_TTL_MS = 5 * 6e4;
25248
+ var CACHE_LIMIT = 16;
25249
+ var CodexHistoryWindows = class {
25250
+ constructor(call, logger) {
25251
+ this.call = call;
25252
+ this.logger = logger;
25253
+ }
25254
+ windows = /* @__PURE__ */ new Map();
25255
+ async window(threadId, metadata, beforeSeq, maxMessages) {
25256
+ const updatedAt = typeof metadata.updatedAt === "number" ? metadata.updatedAt : void 0;
25257
+ const cached = this.windows.get(threadId);
25258
+ const fresh = cached !== void 0 && (updatedAt === void 0 || cached.updatedAt === void 0 || cached.updatedAt === updatedAt) && Date.now() - cached.at < CACHE_TTL_MS;
25259
+ if (fresh && cached !== void 0) {
25260
+ cached.at = Date.now();
25261
+ await this.ensureOlder(threadId, cached, beforeSeq, maxMessages);
25262
+ this.logger?.debug("codex history cache hit", {
25263
+ threadId,
25264
+ turns: cached.loaded.size,
25265
+ records: cached.history.entries.length
25266
+ });
25267
+ return cached;
25268
+ }
25269
+ if (cached !== void 0) {
25270
+ const appended = await this.appendNewTurns(threadId, cached, updatedAt);
25271
+ if (appended !== void 0) {
25272
+ await this.ensureOlder(threadId, appended, beforeSeq, maxMessages);
25273
+ this.remember(threadId, appended);
25274
+ this.logger?.debug("codex history cache appended", {
25275
+ threadId,
25276
+ turns: appended.loaded.size,
25277
+ records: appended.history.entries.length
25278
+ });
25279
+ return appended;
25280
+ }
25281
+ }
25282
+ const built = await this.rebuild(threadId, metadata, cached?.acceptedPageSize);
25283
+ await this.ensureOlder(threadId, built, beforeSeq, maxMessages);
25284
+ this.remember(threadId, built);
25285
+ return built;
25286
+ }
25287
+ async rebuild(threadId, metadata, acceptedPageSize) {
25288
+ const startedAt = performance.now();
25289
+ const index = await this.readTurnIndex(threadId);
25290
+ const window = {
25291
+ metadata,
25292
+ turnNumbers: index.numbers,
25293
+ turnStartSeq: index.turnStartSeq,
25294
+ totalTurns: index.total,
25295
+ totalEvents: index.totalEvents,
25296
+ loaded: /* @__PURE__ */ new Map(),
25297
+ history: projectCodexNativeHistory({ ...metadata, turns: [] }, `codex:${threadId}`, "legacy", {
25298
+ turnNumbers: index.numbers
25299
+ }),
25300
+ ...typeof metadata.updatedAt === "number" ? { updatedAt: metadata.updatedAt } : {},
25301
+ ...acceptedPageSize === void 0 ? {} : { acceptedPageSize },
25302
+ at: Date.now()
25303
+ };
25304
+ this.logger?.debug("codex history index", {
25305
+ threadId,
25306
+ turns: index.total,
25307
+ pages: index.pages,
25308
+ durationMs: Math.round(performance.now() - startedAt)
25309
+ });
25310
+ await this.loadTail(threadId, window);
25311
+ return window;
25312
+ }
25313
+ async readTurnIndex(threadId) {
25314
+ const numbers = /* @__PURE__ */ new Map();
25315
+ const turnStartSeq = /* @__PURE__ */ new Map();
25316
+ let total = 0;
25317
+ let totalEvents = 0;
25318
+ let pages = 0;
25319
+ let cursor2;
25320
+ for (let page = 0; page < MAX_PAGES; page += 1) {
25321
+ const result = await this.turnPage(threadId, cursor2, "asc", "summary");
25322
+ pages += 1;
25323
+ for (const rawTurn of result.turns) {
25324
+ const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
25325
+ if (turnId === void 0 || numbers.has(turnId)) continue;
25326
+ total += 1;
25327
+ numbers.set(turnId, total);
25328
+ turnStartSeq.set(turnId, totalEvents);
25329
+ totalEvents += projectCodexNativeHistory({ turns: [rawTurn] }, `codex:${threadId}`).entries.length;
25330
+ }
25331
+ cursor2 = result.nextCursor;
25332
+ if (cursor2 === void 0) break;
25333
+ }
25334
+ return { numbers, turnStartSeq, total, totalEvents, pages };
25335
+ }
25336
+ async loadTail(threadId, window) {
25337
+ let cursor2;
25338
+ let fetched = 0;
25339
+ for (let page = 0; page < MAX_PAGES; page += 1) {
25340
+ const result = await this.turnPage(threadId, cursor2, "desc", "full");
25341
+ fetched += result.turns.length;
25342
+ this.addTurns(window, result.turns);
25343
+ cursor2 = result.nextCursor;
25344
+ this.project(threadId, window);
25345
+ if (surfaceRecords(window.history) >= HISTORY_PAGE_SIZES[0] || cursor2 === void 0) break;
25346
+ }
25347
+ window.olderCursor = cursor2;
25348
+ this.logger?.debug("codex history tail", {
25349
+ threadId,
25350
+ turnsFetched: fetched,
25351
+ loadedTurns: window.loaded.size,
25352
+ records: window.history.entries.length,
25353
+ older: window.olderCursor !== void 0
25354
+ });
25355
+ }
25356
+ async ensureOlder(threadId, window, beforeSeq, maxMessages) {
25357
+ if (beforeSeq === void 0) return;
25358
+ const needed = Math.min(maxMessages ?? HISTORY_PAGE_SIZES[0], HISTORY_PAGE_SIZES[0]);
25359
+ const available = () => window.history.entries.filter((entry) => entry.event.seq < beforeSeq && isSurfaceEvent(entry.event.type) && entry.event.surfaceOp === "append").length;
25360
+ let fetched = 0;
25361
+ for (let page = 0; page < MAX_PAGES; page += 1) {
25362
+ if (available() >= needed) break;
25363
+ if (window.olderCursor === void 0) break;
25364
+ const result = await this.turnPage(threadId, window.olderCursor, "desc", "full");
25365
+ fetched += result.turns.length;
25366
+ this.addTurns(window, result.turns);
25367
+ window.olderCursor = result.nextCursor;
25368
+ this.project(threadId, window);
25369
+ }
25370
+ if (fetched > 0) {
25371
+ this.logger?.debug("codex history older", {
25372
+ threadId,
25373
+ beforeSeq,
25374
+ needed,
25375
+ turnsFetched: fetched,
25376
+ loadedTurns: window.loaded.size
25377
+ });
25378
+ }
25379
+ }
25380
+ async appendNewTurns(threadId, cached, updatedAt) {
25381
+ if (cached.newestTurnId === void 0) return void 0;
25382
+ const newer = [];
25383
+ let cursor2;
25384
+ for (let page = 0; page < MAX_PAGES; page += 1) {
25385
+ const result = await this.turnPage(threadId, cursor2, "desc", "full");
25386
+ for (const rawTurn of result.turns) {
25387
+ if (typeof rawTurn.id === "string" && rawTurn.id === cached.newestTurnId) {
25388
+ if (newer.length === 0) {
25389
+ cached.updatedAt = updatedAt;
25390
+ cached.at = Date.now();
25391
+ return cached;
25392
+ }
25393
+ this.addTurns(cached, [...newer].reverse());
25394
+ this.project(threadId, cached);
25395
+ cached.updatedAt = updatedAt;
25396
+ cached.at = Date.now();
25397
+ return cached;
25398
+ }
25399
+ newer.push(rawTurn);
25400
+ }
25401
+ cursor2 = result.nextCursor;
25402
+ if (cursor2 === void 0) return void 0;
25403
+ }
25404
+ return void 0;
25405
+ }
25406
+ addTurns(window, turns) {
25407
+ for (const turn of turns) {
25408
+ const turnId = typeof turn.id === "string" ? turn.id : void 0;
25409
+ if (turnId === void 0) continue;
25410
+ if (!window.turnNumbers.has(turnId)) {
25411
+ window.totalTurns += 1;
25412
+ window.turnNumbers.set(turnId, window.totalTurns);
25413
+ window.turnStartSeq.set(turnId, window.totalEvents);
25414
+ window.totalEvents += projectCodexNativeHistory({ turns: [turn] }, window.history.header.id).entries.length;
25415
+ }
25416
+ window.loaded.set(turnId, turn);
25417
+ }
25418
+ }
25419
+ project(threadId, window) {
25420
+ const turns = [...window.loaded.entries()].sort((left, right) => (window.turnNumbers.get(left[0]) ?? 0) - (window.turnNumbers.get(right[0]) ?? 0)).map(([, turn]) => turn);
25421
+ const first = [...window.loaded.keys()].sort((left, right) => (window.turnNumbers.get(left) ?? 0) - (window.turnNumbers.get(right) ?? 0))[0];
25422
+ const startSeq = first === void 0 ? 0 : window.turnStartSeq.get(first) ?? 0;
25423
+ const history = projectCodexNativeHistory({ ...window.metadata, turns }, `codex:${threadId}`, "legacy", {
25424
+ seq: startSeq,
25425
+ turnNumbers: window.turnNumbers
25426
+ });
25427
+ history.nextTurn = window.totalTurns + 1;
25428
+ window.history = history;
25429
+ const newest = [...window.turnNumbers.entries()].sort((left, right) => right[1] - left[1])[0]?.[0];
25430
+ if (newest !== void 0) window.newestTurnId = newest;
25431
+ }
25432
+ async turnPage(threadId, cursor2, direction, itemsView) {
25433
+ const result = await this.call("thread/turns/list", {
25434
+ threadId,
25435
+ limit: PAGE_LIMIT,
25436
+ sortDirection: direction,
25437
+ itemsView,
25438
+ ...cursor2 === void 0 ? {} : { cursor: cursor2 }
25439
+ });
25440
+ const page = typeof result === "object" && result !== null ? result : {};
25441
+ const turns = Array.isArray(page.data) ? page.data.filter((turn) => typeof turn === "object" && turn !== null && !Array.isArray(turn)) : [];
25442
+ const nextCursor = typeof page.nextCursor === "string" && page.nextCursor.length > 0 ? page.nextCursor : void 0;
25443
+ return { turns, nextCursor };
25444
+ }
25445
+ remember(threadId, window) {
25446
+ if (this.windows.size >= CACHE_LIMIT && !this.windows.has(threadId)) {
25447
+ const oldest = this.windows.keys().next().value;
25448
+ if (oldest !== void 0) this.windows.delete(oldest);
25449
+ }
25450
+ this.windows.set(threadId, window);
25451
+ }
25452
+ };
25453
+ function surfaceRecords(history) {
25454
+ return history.entries.filter((entry) => isSurfaceEvent(entry.event.type) && entry.event.surfaceOp === "append").length;
25455
+ }
25456
+
24991
25457
  // src/codex/domain.ts
24992
25458
  var APPROVAL_TTL_MS = 5 * 6e4;
24993
25459
  var DEFAULT_RESTART_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
@@ -24996,8 +25462,6 @@ var ACCOUNT_PROBE_TIMEOUT_MS = 6e4;
24996
25462
  var CODEX_START_BUDGET_MS = 9e4;
24997
25463
  var CODEX_PAGE_LIMIT2 = 100;
24998
25464
  var MAX_CODEX_PAGES2 = 32;
24999
- var CODEX_HISTORY_PAGE_LIMIT = 25;
25000
- var MAX_CODEX_HISTORY_PAGES = 64;
25001
25465
  var CODEX_DIRECTORY_ENTRY_LIMIT = 500;
25002
25466
  var CodexRemoteDomain = class {
25003
25467
  constructor(config, logger, createAppServer = (binary, targetLogger) => new CodexAppServerClient(binary, targetLogger), restartDelaysMs = DEFAULT_RESTART_DELAYS_MS) {
@@ -25005,6 +25469,7 @@ var CodexRemoteDomain = class {
25005
25469
  this.createAppServer = createAppServer;
25006
25470
  this.restartDelaysMs = restartDelaysMs;
25007
25471
  this.config = config;
25472
+ this.historyWindows = new CodexHistoryWindows((method, params) => this.callUpstream(method, params), logger);
25008
25473
  }
25009
25474
  appServer;
25010
25475
  unsubscribeInbound;
@@ -25014,6 +25479,8 @@ var CodexRemoteDomain = class {
25014
25479
  turnOwners = /* @__PURE__ */ new Map();
25015
25480
  approvals = /* @__PURE__ */ new Map();
25016
25481
  permissionPresets = /* @__PURE__ */ new Map();
25482
+ /** Per-Thread turn index plus the tail-first full-item window. */
25483
+ historyWindows;
25017
25484
  approvalExpiryTimer;
25018
25485
  restartTimer;
25019
25486
  restartAttempt = 0;
@@ -25115,14 +25582,16 @@ var CodexRemoteDomain = class {
25115
25582
  }
25116
25583
  if (call.method === "thread/list") {
25117
25584
  const result = sanitizeThreadList(await this.callUpstream(call.method, call.params));
25585
+ const authority = await this.readWorkspaceAuthority(result.data);
25118
25586
  const authorized = filterThreadListByWorkspaceAuthority(
25119
25587
  result,
25120
- await this.readWorkspaceAuthority(result.data)
25588
+ authority,
25589
+ this.config.showUnprojectedSessions !== false
25121
25590
  );
25122
- return {
25591
+ return rebaseThreadListCwds({
25123
25592
  ...authorized,
25124
25593
  data: uniqueCodexThreadRows(authorized.data)
25125
- };
25594
+ }, authority.roots);
25126
25595
  }
25127
25596
  if (call.method === "thread/start") {
25128
25597
  const cwd = await this.requireCodexWorkspacePath(call.params.cwd);
@@ -25141,21 +25610,15 @@ var CodexRemoteDomain = class {
25141
25610
  }
25142
25611
  const threadId = threadIdFromParams(call.params);
25143
25612
  if (call.method === "dsh/sessionHistory") {
25144
- const thread = await this.readThreadForHistory(connectionId, threadId);
25145
- const page = paginateCodexNativeHistory(
25146
- projectCodexNativeHistory(thread, `codex:${threadId}`),
25147
- {
25148
- beforeSeq: optionalInteger2(call.params.beforeSeq),
25149
- throughSeq: optionalInteger2(call.params.throughSeq),
25150
- maxMessages: optionalInteger2(call.params.maxMessages)
25151
- }
25152
- );
25153
- const activeTurnId2 = typeof page.activeTurnId === "string" ? page.activeTurnId : this.turnOwners.get(threadId)?.turnId;
25154
- return {
25155
- ...page,
25156
- ...activeTurnId2 === void 0 ? {} : { activeTurnId: activeTurnId2 },
25157
- permissionPreset: this.permissionPresets.get(threadId) ?? null
25158
- };
25613
+ const { thread: metadata } = await this.readKnownThread(threadId);
25614
+ const beforeSeq = optionalInteger2(call.params.beforeSeq);
25615
+ const maxMessages = optionalInteger2(call.params.maxMessages);
25616
+ const window = await this.historyWindows.window(threadId, metadata, beforeSeq, maxMessages);
25617
+ return this.historyPage(window, threadId, {
25618
+ beforeSeq,
25619
+ throughSeq: optionalInteger2(call.params.throughSeq),
25620
+ maxMessages
25621
+ });
25159
25622
  }
25160
25623
  if (call.method === "dsh/directoryList") {
25161
25624
  return this.listCodexDirectory(call.params.path);
@@ -25658,103 +26121,60 @@ var CodexRemoteDomain = class {
25658
26121
  return { thread: listed, ...typeof listed.cwd === "string" && listed.cwd.length > 0 ? { cwd: listed.cwd } : {} };
25659
26122
  }
25660
26123
  const thread = extractThread(result);
25661
- if (thread === void 0 || thread.id !== threadId || !isThreadAllowedByWorkspaceAuthority(thread, authority)) {
26124
+ if (thread === void 0 || thread.id !== threadId || !this.threadAllowed(thread, authority)) {
25662
26125
  throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
25663
26126
  }
25664
26127
  return { thread, ...typeof thread.cwd === "string" && thread.cwd.length > 0 ? { cwd: thread.cwd } : {} };
25665
26128
  }
25666
- async readThreadForHistory(connectionId, threadId) {
25667
- const { thread: metadata } = await this.readKnownThread(threadId);
25668
- try {
25669
- return { ...metadata, turns: await this.readThreadTurns(connectionId, threadId, "full") };
25670
- } catch (fullError) {
25671
- if (!isHistoryReadRecoverable(fullError)) throw fullError;
25672
- this.logHistoryFallback(connectionId, "turns-full", fullError);
25673
- }
25674
- try {
25675
- return { ...metadata, turns: await this.readThreadTurns(connectionId, threadId, "summary") };
25676
- } catch (summaryError) {
25677
- if (!isHistoryReadRecoverable(summaryError)) throw summaryError;
25678
- this.logHistoryFallback(connectionId, "turns-summary", summaryError);
25679
- }
25680
- try {
25681
- const legacyResult = await this.callUpstream("thread/read", { threadId, includeTurns: true });
25682
- const legacyThread = extractThread(legacyResult);
25683
- if (legacyThread === void 0 || legacyThread.id !== threadId) {
25684
- throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid Thread history.");
25685
- }
25686
- return legacyThread;
25687
- } catch (legacyError) {
25688
- if (!isHistoryReadRecoverable(legacyError)) throw legacyError;
25689
- this.logHistoryFallback(connectionId, "thread-read-full", legacyError);
25690
- return { ...metadata, turns: [] };
25691
- }
25692
- }
25693
- async readThreadTurns(connectionId, threadId, itemsView) {
25694
- const turns = [];
25695
- let cursor2;
25696
- for (let page = 0; page < MAX_CODEX_HISTORY_PAGES; page += 1) {
25697
- const result = await this.callUpstream("thread/turns/list", {
26129
+ historyPage(window, threadId, request) {
26130
+ const requested = request.maxMessages ?? HISTORY_PAGE_SIZES[0];
26131
+ const start = Math.max(1, Math.min(requested, HISTORY_PAGE_SIZES[0], window.acceptedPageSize ?? requested));
26132
+ const activeTurnId2 = this.turnOwners.get(threadId)?.turnId;
26133
+ for (const size of historyPageSizes(start)) {
26134
+ const page = paginateCodexNativeHistory(window.history, {
26135
+ beforeSeq: request.beforeSeq,
26136
+ throughSeq: request.throughSeq,
26137
+ maxMessages: size
26138
+ });
26139
+ const resolvedActiveTurnId = page.activeTurnId ?? activeTurnId2;
26140
+ const response = {
26141
+ ...page,
26142
+ cursor: window.history.lastSeq,
26143
+ nextTurn: window.totalTurns + 1,
26144
+ hasMore: page.hasMore || window.olderCursor !== void 0,
26145
+ ...resolvedActiveTurnId === void 0 ? {} : { activeTurnId: resolvedActiveTurnId },
26146
+ permissionPreset: this.permissionPresets.get(threadId) ?? null
26147
+ };
26148
+ const bytes = Buffer4.byteLength(JSON.stringify(response), "utf8");
26149
+ this.logger.debug("codex history page", {
25698
26150
  threadId,
25699
- limit: CODEX_HISTORY_PAGE_LIMIT,
25700
- sortDirection: "asc",
25701
- itemsView,
25702
- ...cursor2 === void 0 ? {} : { cursor: cursor2 }
26151
+ requested,
26152
+ size,
26153
+ bytes,
26154
+ budget: HISTORY_BUDGET_BYTES,
26155
+ loadedTurns: window.loaded.size,
26156
+ totalTurns: window.totalTurns
25703
26157
  });
25704
- const pageResult = isRecord12(result) ? result : {};
25705
- for (const rawTurn of array2(pageResult.data)) {
25706
- if (!isRecord12(rawTurn)) continue;
25707
- const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
25708
- const items = rawTurn.itemsView === "full" || turnId === void 0 ? array2(rawTurn.items) : await this.readThreadItems(connectionId, threadId, turnId, array2(rawTurn.items));
25709
- turns.push({ ...rawTurn, items });
26158
+ if (bytes <= HISTORY_BUDGET_BYTES) {
26159
+ window.acceptedPageSize = size;
26160
+ return response;
25710
26161
  }
25711
- cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
25712
- if (cursor2 === void 0) break;
25713
26162
  }
25714
- return turns;
25715
- }
25716
- async readThreadItems(connectionId, threadId, turnId, fallbackItems) {
25717
- const items = [];
25718
- let cursor2;
25719
- try {
25720
- for (let page = 0; page < MAX_CODEX_HISTORY_PAGES; page += 1) {
25721
- const result = await this.callUpstream("thread/items/list", {
25722
- threadId,
25723
- turnId,
25724
- limit: CODEX_HISTORY_PAGE_LIMIT,
25725
- sortDirection: "asc",
25726
- ...cursor2 === void 0 ? {} : { cursor: cursor2 }
25727
- });
25728
- const pageResult = isRecord12(result) ? result : {};
25729
- for (const entry of array2(pageResult.data)) {
25730
- if (isRecord12(entry) && entry.item !== void 0) items.push(entry.item);
25731
- }
25732
- cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
25733
- if (cursor2 === void 0) break;
25734
- }
25735
- } catch (error) {
25736
- if (!isHistoryReadRecoverable(error)) throw error;
25737
- this.logHistoryFallback(connectionId, "items", error);
25738
- return fallbackItems;
25739
- }
25740
- return items;
25741
- }
25742
- logHistoryFallback(connectionId, stage, error) {
25743
- this.logger.warn("Codex history read fallback", {
25744
- connectionId: maskId(connectionId),
25745
- stage,
25746
- code: errorCode4(error)
25747
- });
26163
+ throw new RpcError("RESPONSE_TOO_LARGE", "The Codex history page is too large for the secure Remote channel.");
25748
26164
  }
25749
26165
  async assertResultThreadAllowed(result) {
25750
26166
  const thread = extractThread(result);
25751
26167
  if (thread === void 0 || typeof thread.id !== "string") {
25752
26168
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread.");
25753
26169
  }
25754
- if (!isThreadAllowedByWorkspaceAuthority(thread, await this.readWorkspaceAuthority())) {
26170
+ if (!this.threadAllowed(thread, await this.readWorkspaceAuthority())) {
25755
26171
  throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
25756
26172
  }
25757
26173
  }
26174
+ /** The authority check, widened when unprojected Sessions are exposed. */
26175
+ threadAllowed(thread, authority) {
26176
+ return this.config.showUnprojectedSessions !== false || isThreadAllowedByWorkspaceAuthority(thread, authority);
26177
+ }
25758
26178
  claimTurn(threadId, connectionId, method, turnId) {
25759
26179
  const owner = this.turnOwners.get(threadId);
25760
26180
  const peerDeviceId = this.peerDeviceIds.get(connectionId) ?? connectionId;
@@ -25895,7 +26315,7 @@ var CodexRemoteDomain = class {
25895
26315
  }));
25896
26316
  const thread = result.data.find((item) => item.id === threadId);
25897
26317
  if (thread !== void 0) {
25898
- return isThreadAllowedByWorkspaceAuthority(thread, authority) ? thread : void 0;
26318
+ return this.threadAllowed(thread, authority) ? thread : void 0;
25899
26319
  }
25900
26320
  cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
25901
26321
  if (cursor2 === void 0) break;
@@ -25906,6 +26326,14 @@ var CodexRemoteDomain = class {
25906
26326
  if (!isAbsolute2(path)) {
25907
26327
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
25908
26328
  }
26329
+ if (this.config.showUnprojectedSessions !== false) {
26330
+ try {
26331
+ const candidate2 = await realpath(path);
26332
+ if ((await stat4(candidate2)).isDirectory()) return candidate2;
26333
+ } catch {
26334
+ }
26335
+ throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
26336
+ }
25909
26337
  const paths = await this.listCodexWorkspacePaths();
25910
26338
  const lexicalCandidate = resolve(path);
25911
26339
  let candidate;
@@ -26006,7 +26434,7 @@ var CodexRemoteDomain = class {
26006
26434
  return paths;
26007
26435
  }
26008
26436
  };
26009
- function codexBinaryCandidates(configured, hostPlatform = process.platform, userHome = homedir2(), localAppData = process.env.LOCALAPPDATA) {
26437
+ function codexBinaryCandidates(configured, hostPlatform = process.platform, userHome = homedir4(), localAppData = process.env.LOCALAPPDATA) {
26010
26438
  if (configured !== "codex") return [configured];
26011
26439
  if (hostPlatform === "win32") {
26012
26440
  return [.../* @__PURE__ */ new Set([
@@ -26042,12 +26470,12 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
26042
26470
  ])];
26043
26471
  }
26044
26472
  function bundledWindowsCodex(userHome) {
26045
- return bundledWindowsCodexAt(join7(userHome, "AppData", "Local"));
26473
+ return bundledWindowsCodexAt(join9(userHome, "AppData", "Local"));
26046
26474
  }
26047
26475
  function bundledWindowsCodexAt(localAppData) {
26048
- const bin = join7(localAppData, "OpenAI", "Codex", "bin");
26476
+ const bin = join9(localAppData, "OpenAI", "Codex", "bin");
26049
26477
  try {
26050
- const newest = readdirSync2(bin, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join7(bin, entry.name, "codex.exe")).filter((candidate2) => existsSync3(candidate2)).map((candidate2) => ({ candidate: candidate2, modified: statSync2(candidate2).mtimeMs })).sort((left, right) => right.modified - left.modified);
26478
+ const newest = readdirSync2(bin, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join9(bin, entry.name, "codex.exe")).filter((candidate2) => existsSync3(candidate2)).map((candidate2) => ({ candidate: candidate2, modified: statSync2(candidate2).mtimeMs })).sort((left, right) => right.modified - left.modified);
26051
26479
  const candidate = newest[0]?.candidate;
26052
26480
  return candidate === void 0 ? [] : [candidate];
26053
26481
  } catch {
@@ -26120,13 +26548,30 @@ function sanitizeThreadList(result) {
26120
26548
  ...typeof result.backwardsCursor === "string" && result.backwardsCursor.length > 0 ? { backwardsCursor: result.backwardsCursor } : {}
26121
26549
  };
26122
26550
  }
26123
- function filterThreadListByWorkspaceAuthority(result, authority) {
26551
+ function filterThreadListByWorkspaceAuthority(result, authority, includeUnprojected = false) {
26124
26552
  if (!isRecord12(result) || !Array.isArray(result.data)) {
26125
26553
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
26126
26554
  }
26127
26555
  return {
26128
26556
  ...result,
26129
- data: result.data.map((record7) => isRecord12(record7) ? record7 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
26557
+ data: result.data.map((record7) => isRecord12(record7) ? record7 : void 0).filter((thread) => thread !== void 0 && (includeUnprojected || isThreadAllowedByWorkspaceAuthority(thread, authority)))
26558
+ };
26559
+ }
26560
+ function rebaseThreadListCwds(result, roots) {
26561
+ if (process.platform !== "win32" || roots.length === 0) return result;
26562
+ const ordered = [...roots].sort((left, right) => right.length - left.length);
26563
+ return {
26564
+ ...result,
26565
+ data: result.data.map((thread) => {
26566
+ const cwd = typeof thread.cwd === "string" ? thread.cwd : void 0;
26567
+ if (cwd === void 0) return thread;
26568
+ const root = ordered.find((candidate) => containsCodexPath(candidate, cwd));
26569
+ if (root === void 0) return thread;
26570
+ const normalizedRoot = normalizeCodexPathForCompare(root);
26571
+ const normalizedCwd = normalizeCodexPathForCompare(cwd);
26572
+ const rebased = normalizedCwd === normalizedRoot ? root : normalizedCwd.startsWith(`${normalizedRoot}/`) || normalizedCwd.startsWith(`${normalizedRoot}\\`) ? `${root}${cwd.slice(root.length)}` : cwd;
26573
+ return rebased === cwd ? thread : { ...thread, cwd: rebased };
26574
+ })
26130
26575
  };
26131
26576
  }
26132
26577
  function sanitizeProject(value) {
@@ -26283,12 +26728,6 @@ function canTryNextBinary(error) {
26283
26728
  function isRecord12(value) {
26284
26729
  return typeof value === "object" && value !== null && !Array.isArray(value);
26285
26730
  }
26286
- function array2(value) {
26287
- return Array.isArray(value) ? value : [];
26288
- }
26289
- function maskId(value) {
26290
- return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
26291
- }
26292
26731
  function isActiveWriterMessage(message) {
26293
26732
  return message.toLowerCase().includes("active writer");
26294
26733
  }
@@ -27402,12 +27841,12 @@ function isOversizedListing(value) {
27402
27841
 
27403
27842
  // src/remote-directory-browser.ts
27404
27843
  import { readdir as readdir3, stat as stat5 } from "node:fs/promises";
27405
- import { homedir as homedir3, platform as platform2 } from "node:os";
27844
+ import { homedir as homedir5, platform as platform2 } from "node:os";
27406
27845
  import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute3, parse, resolve as resolve2 } from "node:path";
27407
27846
  var MAX_ENTRIES = 500;
27408
27847
  async function listRemoteDirectory(path, signal) {
27409
27848
  signal?.throwIfAborted();
27410
- const home = resolve2(homedir3());
27849
+ const home = resolve2(homedir5());
27411
27850
  const target2 = path === void 0 || path.trim() === "" ? home : resolve2(path);
27412
27851
  if (!isAbsolute3(target2)) throw new Error("The remote directory path must be absolute.");
27413
27852
  const rows = await readdir3(target2, { withFileTypes: true });
@@ -28233,7 +28672,7 @@ function frameSessionId(frame) {
28233
28672
  return typeof payload.sessionId === "string" && payload.sessionId.length > 0 ? payload.sessionId : void 0;
28234
28673
  }
28235
28674
  function withTimeout(promise, ms, message) {
28236
- return new Promise((resolve4, reject) => {
28675
+ return new Promise((resolve5, reject) => {
28237
28676
  const timer = setTimeout(() => {
28238
28677
  reject(new RpcError("TIMEOUT", message, void 0, true));
28239
28678
  }, ms);
@@ -28241,7 +28680,7 @@ function withTimeout(promise, ms, message) {
28241
28680
  promise.then(
28242
28681
  (value) => {
28243
28682
  clearTimeout(timer);
28244
- resolve4(value);
28683
+ resolve5(value);
28245
28684
  },
28246
28685
  (error) => {
28247
28686
  clearTimeout(timer);
@@ -28271,7 +28710,8 @@ function concatChunks2(chunks, totalBytes) {
28271
28710
  }
28272
28711
 
28273
28712
  // src/harness-remote-bridge.ts
28274
- import { Buffer as Buffer4 } from "node:buffer";
28713
+ import { Buffer as Buffer5 } from "node:buffer";
28714
+ import { isAbsolute as isAbsolute4, join as join10, relative as relative2, resolve as resolve3, sep } from "node:path";
28275
28715
  var endpointSchema = external_exports.string().min(1).max(128).regex(/^(?:\$events(?:\/result)?|[A-Za-z0-9_$.-]+\/[A-Za-z0-9_$.-]+)$/);
28276
28716
  var callSchema3 = external_exports.object({ endpoint: endpointSchema, payload: external_exports.unknown() }).strict();
28277
28717
  var streamOpenSchema3 = external_exports.object({
@@ -28320,9 +28760,8 @@ var MAX_ACTIVE_STREAMS2 = MAX_ALPHA_STREAMS_PER_CONNECTION;
28320
28760
  var MAX_ACTIVE_TRANSFERS2 = MAX_ACTIVE_TRANSFERS_PER_DIRECTION;
28321
28761
  var INLINE_TRANSFER_RESPONSE_BYTES3 = 2 * 1024 * 1024;
28322
28762
  var STREAM_FRAME_ENVELOPE_BYTES = 4 * 1024;
28323
- var RESPONSE_ENVELOPE_BYTES = 8 * 1024;
28324
- var HISTORY_PAGE_SIZES = [120, 80, 50, 30, 20, 12, 6, 3, 1];
28325
- var MAX_HISTORY_PAGE_MESSAGES = 500;
28763
+ var RESPONSE_ENVELOPE_BYTES2 = 8 * 1024;
28764
+ var WORKSPACE_FILE_ENDPOINTS = /* @__PURE__ */ new Set(["workspaceFiles/stat", "workspaceFiles/read", "workspaceFiles/readBytes", "workspaceFiles/list"]);
28326
28765
  var REMOTE_HISTORY_CALL_ENDPOINTS = /* @__PURE__ */ new Set(["session/page", "session/history"]);
28327
28766
  var REMOTE_HISTORY_STREAM_ENDPOINTS = /* @__PURE__ */ new Set(["session/follow"]);
28328
28767
  var MAX_CACHED_HISTORY_PAGES = 64;
@@ -28345,6 +28784,9 @@ var HARNESS_REMOTE_ALLOWLIST = [
28345
28784
  "goals/edit",
28346
28785
  "goals/pause",
28347
28786
  "goals/resume",
28787
+ "job/follow",
28788
+ "job/kill",
28789
+ "job/list",
28348
28790
  "llm/discoverModels",
28349
28791
  "llm/listConfigurableProviders",
28350
28792
  "llm/listProviders",
@@ -28403,19 +28845,22 @@ var HARNESS_REMOTE_ALLOWLIST = [
28403
28845
  ];
28404
28846
  var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
28405
28847
  var HarnessRemoteBridge = class {
28406
- constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map()), codexWorkspace) {
28848
+ constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map()), codexWorkspace, sessionHeaderCwd) {
28407
28849
  this.gateway = gateway;
28408
28850
  this.publish = publish;
28409
28851
  this.logger = logger;
28410
28852
  this.harnessVersion = harnessVersion;
28411
28853
  this.terminal = terminal;
28412
28854
  this.codexWorkspace = codexWorkspace;
28855
+ this.sessionHeaderCwd = sessionHeaderCwd;
28413
28856
  }
28414
28857
  streams = /* @__PURE__ */ new Map();
28415
28858
  incomingTransfers = /* @__PURE__ */ new Map();
28416
28859
  outgoingTransfers = /* @__PURE__ */ new Map();
28417
28860
  /** Last page size that fit one secure message, per session, so probing is paid once. */
28418
28861
  historyPageSizes = /* @__PURE__ */ new Map();
28862
+ /** Last working directory read per Session, so a file open does not re-project on every chunk. */
28863
+ sessionWorkingDirectories = /* @__PURE__ */ new Map();
28419
28864
  /**
28420
28865
  * Answer one remote Gateway call.
28421
28866
  *
@@ -28456,8 +28901,12 @@ var HarnessRemoteBridge = class {
28456
28901
  const signal = AbortSignal.timeout(6e4);
28457
28902
  try {
28458
28903
  const nativePayload = normalizeWorkspaceRequestPayload(params.endpoint, params.payload, this.harnessVersion);
28459
- const nativeResult = REMOTE_HISTORY_CALL_ENDPOINTS.has(params.endpoint) ? await this.dispatchHistoryPage(params.endpoint, nativePayload, signal) : params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.gateway.dispatch(params.endpoint, nativePayload, signal);
28904
+ const nativeResult = REMOTE_HISTORY_CALL_ENDPOINTS.has(params.endpoint) ? await this.dispatchHistoryPage(params.endpoint, nativePayload, signal) : params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.dispatchWorkspaceFile(params.endpoint, nativePayload, signal);
28460
28905
  const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
28906
+ if (params.endpoint === "workspace/create" && isInvalidWorkspacePath(result)) {
28907
+ const recovered = await this.recoverWorkspaceCreate(params.payload, signal);
28908
+ if (recovered !== void 0) return recovered;
28909
+ }
28461
28910
  this.logger?.debug("harness remote call ok", {
28462
28911
  endpoint: params.endpoint,
28463
28912
  durationMs: Math.round(performance.now() - startedAt)
@@ -28465,6 +28914,10 @@ var HarnessRemoteBridge = class {
28465
28914
  return reservation === void 0 ? result : this.terminal.result(params.endpoint, params.payload, result, reservation);
28466
28915
  } catch (error) {
28467
28916
  if (reservation !== void 0) this.terminal.result(params.endpoint, params.payload, { ok: false, error: { code: "FAILED", message: "", details: {} } }, reservation);
28917
+ if (params.endpoint === "workspace/create" && isInvalidWorkspacePath(error)) {
28918
+ const recovered = await this.recoverWorkspaceCreate(params.payload, signal);
28919
+ if (recovered !== void 0) return recovered;
28920
+ }
28468
28921
  if (params.endpoint === "directoryPicker/list") {
28469
28922
  const result = await this.directoryList(params.payload, signal);
28470
28923
  this.logger?.debug("harness remote call ok", {
@@ -28490,6 +28943,43 @@ var HarnessRemoteBridge = class {
28490
28943
  const params = directoryListSchema2.parse(args);
28491
28944
  return { ok: true, value: await listRemoteDirectory(params.path, signal) };
28492
28945
  }
28946
+ /**
28947
+ * Resolve an existing Workspace when `workspace/create` cannot realpath a
28948
+ * deleted directory.
28949
+ *
28950
+ * DSH keeps a registered Workspace open after its directory disappears, but
28951
+ * `workspace/create` canonicalizes the path first and fails with ENOENT. The
28952
+ * Client can still open the Workspace it sees in the Host's list; resolve that
28953
+ * list by lexical path and return the existing row instead of failing.
28954
+ * @param payload - workspace/create request payload.
28955
+ * @param signal - call lifetime.
28956
+ * @returns an existing-workspace result, or undefined when none matches.
28957
+ */
28958
+ async recoverWorkspaceCreate(payload, signal) {
28959
+ const path = requestArgs(payload).path;
28960
+ if (typeof path !== "string" || path === "") return void 0;
28961
+ let source;
28962
+ try {
28963
+ source = await this.gateway.open("workspace/follow", { args: {} }, signal);
28964
+ } catch {
28965
+ return void 0;
28966
+ }
28967
+ const iterator = source[Symbol.asyncIterator]();
28968
+ let first;
28969
+ try {
28970
+ first = await iterator.next();
28971
+ } catch {
28972
+ return void 0;
28973
+ } finally {
28974
+ await iterator.return?.().catch(() => void 0);
28975
+ }
28976
+ const frame = isRecord14(first.value) ? first.value : {};
28977
+ const baseline = isRecord14(frame.value) ? frame.value : frame;
28978
+ const items = Array.isArray(baseline.items) ? baseline.items : [];
28979
+ const target2 = workspacePathIdentity(path);
28980
+ const workspace = items.find((item) => isRecord14(item) && typeof item.path === "string" && workspacePathIdentity(item.path) === target2);
28981
+ return workspace === void 0 ? void 0 : { ok: true, value: { workspace, created: false } };
28982
+ }
28493
28983
  openTransfer(input2) {
28494
28984
  this.pruneTransfers();
28495
28985
  const params = transferOpenSchema3.parse(input2);
@@ -28583,7 +29073,7 @@ var HarnessRemoteBridge = class {
28583
29073
  return {
28584
29074
  transferId: params.transferId,
28585
29075
  index: params.index,
28586
- data: Buffer4.from(transfer.bytes.subarray(start, end)).toString("base64")
29076
+ data: Buffer5.from(transfer.bytes.subarray(start, end)).toString("base64")
28587
29077
  };
28588
29078
  }
28589
29079
  closeTransfer(input2) {
@@ -28703,7 +29193,7 @@ var HarnessRemoteBridge = class {
28703
29193
  hasMore: value?.hasMore,
28704
29194
  bytes
28705
29195
  });
28706
- if (bytes <= MAX_SECURE_MESSAGE_BYTES - RESPONSE_ENVELOPE_BYTES) {
29196
+ if (bytes <= MAX_SECURE_MESSAGE_BYTES - RESPONSE_ENVELOPE_BYTES2) {
28707
29197
  this.rememberHistoryPageSize(key, size);
28708
29198
  return wire;
28709
29199
  }
@@ -28722,7 +29212,7 @@ var HarnessRemoteBridge = class {
28722
29212
  const key = historyPageKey(payload);
28723
29213
  const requested = requestedMaxMessages(payload) ?? HISTORY_PAGE_SIZES[0];
28724
29214
  const remembered = this.historyPageSizes.get(key);
28725
- const start = Math.max(1, Math.min(requested, MAX_HISTORY_PAGE_MESSAGES, remembered ?? requested));
29215
+ const start = Math.max(1, Math.min(requested, HISTORY_PAGE_SIZES[0], remembered ?? requested));
28726
29216
  this.logger?.debug("history window", { key: key.slice(0, 120), requested, remembered, start });
28727
29217
  return { key, sizes: historyPageSizes(start) };
28728
29218
  }
@@ -28733,6 +29223,68 @@ var HarnessRemoteBridge = class {
28733
29223
  }
28734
29224
  this.historyPageSizes.set(key, size);
28735
29225
  }
29226
+ /**
29227
+ * Dispatch a workspace-file call, re-rooting a path written against the Session's current
29228
+ * directory onto the header root the Host resolves against.
29229
+ *
29230
+ * DSH names the workspace root from the Session header, so a Session that changed its working
29231
+ * directory mid-run resolves relative links (and deliverables built from the root) against the
29232
+ * stale header. The live `workingDirectory` projection is the correction; when the re-rooted
29233
+ * path misses, the original path is tried so a file created before the change still opens.
29234
+ */
29235
+ async dispatchWorkspaceFile(endpoint, payload, signal) {
29236
+ if (!WORKSPACE_FILE_ENDPOINTS.has(endpoint) || this.sessionHeaderCwd === void 0) {
29237
+ return this.gateway.dispatch(endpoint, payload, signal);
29238
+ }
29239
+ const location = workspaceFileLocation(payload);
29240
+ if (location === void 0) return this.gateway.dispatch(endpoint, payload, signal);
29241
+ const headerCwd = this.sessionHeaderCwd(location.sessionId);
29242
+ if (headerCwd === void 0) return this.gateway.dispatch(endpoint, payload, signal);
29243
+ const liveCwd = await this.sessionWorkingDirectory(location.sessionId, signal);
29244
+ if (liveCwd === void 0) return this.gateway.dispatch(endpoint, payload, signal);
29245
+ const nextPath = rerootWorkspacePath(headerCwd, liveCwd, location.path);
29246
+ if (nextPath === void 0 || nextPath === location.path) return this.gateway.dispatch(endpoint, payload, signal);
29247
+ const rewritten = replaceWorkspaceFilePath(payload, nextPath);
29248
+ if (rewritten === payload) return this.gateway.dispatch(endpoint, payload, signal);
29249
+ const rerooted = await this.gateway.dispatch(endpoint, rewritten, signal);
29250
+ if (!workspaceFileMiss(endpoint, rerooted)) {
29251
+ this.logger?.debug("workspace file path re-rooted", {
29252
+ endpoint,
29253
+ headerCwd,
29254
+ liveCwd,
29255
+ path: location.path,
29256
+ nextPath
29257
+ });
29258
+ return rerooted;
29259
+ }
29260
+ this.logger?.debug("workspace file path re-root missed; using the original path", {
29261
+ endpoint,
29262
+ path: location.path,
29263
+ nextPath
29264
+ });
29265
+ return this.gateway.dispatch(endpoint, payload, signal);
29266
+ }
29267
+ /** Read the Session's live working directory from its projections, cached briefly. */
29268
+ async sessionWorkingDirectory(sessionId, signal) {
29269
+ const cached = this.sessionWorkingDirectories.get(sessionId);
29270
+ if (cached !== void 0 && Date.now() - cached.at < 5e3) return cached.cwd;
29271
+ let cwd;
29272
+ try {
29273
+ const result = await this.gateway.dispatch("session/projections", { args: { request: { sessionId } } }, signal);
29274
+ const value = result.ok ? result.value : void 0;
29275
+ const values = isRecord14(value) && isRecord14(value.values) ? value.values : void 0;
29276
+ const raw = values?.workingDirectory;
29277
+ if (typeof raw === "string" && raw.length > 0) cwd = raw;
29278
+ } catch {
29279
+ cwd = void 0;
29280
+ }
29281
+ if (this.sessionWorkingDirectories.size >= MAX_CACHED_HISTORY_PAGES && !this.sessionWorkingDirectories.has(sessionId)) {
29282
+ const oldest = this.sessionWorkingDirectories.keys().next().value;
29283
+ if (oldest !== void 0) this.sessionWorkingDirectories.delete(oldest);
29284
+ }
29285
+ this.sessionWorkingDirectories.set(sessionId, { cwd, at: Date.now() });
29286
+ return cwd;
29287
+ }
28736
29288
  closeStream(input2) {
28737
29289
  const params = streamCloseSchema3.parse(input2);
28738
29290
  const active = this.streams.get(params.streamId);
@@ -28821,11 +29373,6 @@ function windowedRequest(request, maxMessages) {
28821
29373
  }
28822
29374
  return next;
28823
29375
  }
28824
- function historyPageSizes(start) {
28825
- const sizes = [start];
28826
- for (const size of HISTORY_PAGE_SIZES) if (size < start) sizes.push(size);
28827
- return sizes;
28828
- }
28829
29376
  function historyRequestOf(payload) {
28830
29377
  let node = payload;
28831
29378
  for (let depth = 0; depth < 4; depth += 1) {
@@ -28850,6 +29397,64 @@ function requestedMaxMessages(payload) {
28850
29397
  const value = historyRequestOf(payload)?.maxMessages;
28851
29398
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
28852
29399
  }
29400
+ function workspaceFileLocation(payload) {
29401
+ if (!isRecord14(payload)) return void 0;
29402
+ if (Array.isArray(payload.args)) {
29403
+ if (payload.args.length >= 2 && typeof payload.args[0] === "string" && typeof payload.args[1] === "string") {
29404
+ return { sessionId: payload.args[0], path: payload.args[1] };
29405
+ }
29406
+ const first = payload.args[0];
29407
+ if (isRecord14(first)) return workspaceFileLocation(first);
29408
+ return void 0;
29409
+ }
29410
+ const candidate = isRecord14(payload.args) ? payload.args : isRecord14(payload.request) ? payload.request : payload;
29411
+ const sessionId = [candidate.workspaceFileScopeId, candidate.workspaceFileScope, candidate.sessionId].find((value) => typeof value === "string" && value.length > 0);
29412
+ const path = candidate.path;
29413
+ if (typeof sessionId !== "string" || typeof path !== "string" || path.length === 0) return void 0;
29414
+ return { sessionId, path };
29415
+ }
29416
+ function replaceWorkspaceFilePath(payload, nextPath) {
29417
+ if (!isRecord14(payload)) return payload;
29418
+ if (Array.isArray(payload.args)) {
29419
+ if (payload.args.length >= 2 && typeof payload.args[0] === "string" && typeof payload.args[1] === "string") {
29420
+ const args = [...payload.args];
29421
+ args[1] = nextPath;
29422
+ return { ...payload, args };
29423
+ }
29424
+ const first = payload.args[0];
29425
+ if (!isRecord14(first)) return payload;
29426
+ return { ...payload, args: [{ ...first, path: nextPath }, ...payload.args.slice(1)] };
29427
+ }
29428
+ if (isRecord14(payload.args)) return { ...payload, args: { ...payload.args, path: nextPath } };
29429
+ if (isRecord14(payload.request)) return { ...payload, request: { ...payload.request, path: nextPath } };
29430
+ return { ...payload, path: nextPath };
29431
+ }
29432
+ function workspaceFileMiss(endpoint, result) {
29433
+ if (!result.ok) return true;
29434
+ if (endpoint !== "workspaceFiles/stat") return false;
29435
+ const value = result.value;
29436
+ return isRecord14(value) && value.exists === false;
29437
+ }
29438
+ function rerootWorkspacePath(headerCwd, liveCwd, target2) {
29439
+ if (samePath(headerCwd, liveCwd)) return void 0;
29440
+ if (isAbsolute4(target2)) {
29441
+ const suffix = relative2(headerCwd, target2);
29442
+ if (suffix === "" || suffix === "." || isAbsolute4(suffix) || suffix.startsWith(`..${sep}`) || suffix === "..") return void 0;
29443
+ return join10(liveCwd, suffix);
29444
+ }
29445
+ const prefix = relative2(headerCwd, liveCwd);
29446
+ if (prefix !== "" && prefix !== "." && !isAbsolute4(prefix) && !prefix.startsWith(`..${sep}`) && prefix !== "..") {
29447
+ return join10(prefix, target2);
29448
+ }
29449
+ return join10(liveCwd, target2);
29450
+ }
29451
+ function samePath(left, right) {
29452
+ const normalize = (value) => {
29453
+ const trimmed = value.replace(/[\\/]+$/u, "");
29454
+ return process.platform === "win32" ? trimmed.toLowerCase() : trimmed;
29455
+ };
29456
+ return normalize(left) === normalize(right);
29457
+ }
28853
29458
  function historyPageKey(payload) {
28854
29459
  const request = historyRequestOf(payload);
28855
29460
  if (request === void 0) return "default";
@@ -28862,10 +29467,10 @@ function historyPageKey(payload) {
28862
29467
  }
28863
29468
  }
28864
29469
  function responseBytes(result) {
28865
- return Buffer4.byteLength(JSON.stringify(result), "utf8");
29470
+ return Buffer5.byteLength(JSON.stringify(result), "utf8");
28866
29471
  }
28867
29472
  function streamFrameBytes(frame) {
28868
- return Buffer4.byteLength(JSON.stringify(frame), "utf8");
29473
+ return Buffer5.byteLength(JSON.stringify(frame), "utf8");
28869
29474
  }
28870
29475
  function remainingIterable(iterator) {
28871
29476
  return { [Symbol.asyncIterator]: () => iterator };
@@ -28925,7 +29530,7 @@ async function dispatchCommandForHost(gateway, payload, signal, harnessVersion)
28925
29530
  );
28926
29531
  }
28927
29532
  function decodeCanonicalBase643(value) {
28928
- const bytes = Buffer4.from(value, "base64");
29533
+ const bytes = Buffer5.from(value, "base64");
28929
29534
  if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
28930
29535
  return bytes;
28931
29536
  }
@@ -28955,6 +29560,17 @@ function record6(value) {
28955
29560
  function isRecord14(value) {
28956
29561
  return typeof value === "object" && value !== null && !Array.isArray(value);
28957
29562
  }
29563
+ function workspacePathIdentity(path) {
29564
+ const normalized = resolve3(path).replace(/[\\/]+$/, "");
29565
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
29566
+ }
29567
+ function isInvalidWorkspacePath(input2) {
29568
+ const error = isRecord14(input2) && input2.ok === false && isRecord14(input2.error) ? input2.error : input2;
29569
+ if (!isRecord14(error)) return false;
29570
+ const code = typeof error.code === "string" ? error.code : "";
29571
+ const message = typeof error.message === "string" ? error.message : "";
29572
+ return code === "workspace/invalid-path" || message.includes("cannot create a Workspace at") && message.includes("ENOENT");
29573
+ }
28958
29574
 
28959
29575
  // src/acp.ts
28960
29576
  import { spawn as spawn3 } from "node:child_process";
@@ -29044,7 +29660,7 @@ var StdioAcpAdapter = class {
29044
29660
  const c = this.ensure();
29045
29661
  const id4 = this.nextId++;
29046
29662
  c.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id4, method, params }) + "\n");
29047
- return new Promise((resolve4, reject) => {
29663
+ return new Promise((resolve5, reject) => {
29048
29664
  let buf = "";
29049
29665
  const onData = async (d) => {
29050
29666
  buf += d;
@@ -29055,7 +29671,7 @@ var StdioAcpAdapter = class {
29055
29671
  const m = JSON.parse(l);
29056
29672
  if (m.id === id4) {
29057
29673
  c.stdout.off("data", onData);
29058
- m.error ? reject(new Error(m.error.message ?? "ACP error")) : resolve4(m.result);
29674
+ m.error ? reject(new Error(m.error.message ?? "ACP error")) : resolve5(m.result);
29059
29675
  } else if (m.method && onNotification) await onNotification(m.params);
29060
29676
  } catch {
29061
29677
  }
@@ -29073,7 +29689,7 @@ import { execFileSync as execFileSync2 } from "node:child_process";
29073
29689
  // src/codex-workspace-bridge.ts
29074
29690
  import { spawn as spawn4 } from "node:child_process";
29075
29691
  import { realpath as realpath2, lstat, readdir as readdir4, readFile as readFile5, stat as stat6, watch } from "node:fs/promises";
29076
- import { isAbsolute as isAbsolute4, join as join8, relative as relative2, resolve as resolve3 } from "node:path";
29692
+ import { isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as resolve4 } from "node:path";
29077
29693
  var MAX_READ_BYTES = 4 * 1024 * 1024;
29078
29694
  var MAX_INPUT_BYTES = 64 * 1024;
29079
29695
  var MAX_COLS = 240;
@@ -29104,7 +29720,7 @@ var AsyncQueue = class {
29104
29720
  const value = this.values.shift();
29105
29721
  if (value !== void 0) return Promise.resolve({ done: false, value });
29106
29722
  if (this.ended) return Promise.resolve({ done: true, value: void 0 });
29107
- return new Promise((resolve4) => this.waiters.push(resolve4));
29723
+ return new Promise((resolve5) => this.waiters.push(resolve5));
29108
29724
  }
29109
29725
  [Symbol.asyncIterator]() {
29110
29726
  return this;
@@ -29160,7 +29776,7 @@ var CodexWorkspaceBridge = class {
29160
29776
  const entries = await readdir4(target2, { withFileTypes: true });
29161
29777
  const result = [];
29162
29778
  for (const entry of entries.slice(0, 500)) {
29163
- const item = join8(target2, entry.name);
29779
+ const item = join11(target2, entry.name);
29164
29780
  const info2 = await lstat(item);
29165
29781
  if (info2.isSymbolicLink()) continue;
29166
29782
  result.push({ name: entry.name, type: info2.isDirectory() ? "directory" : info2.isFile() ? "file" : "other", ...info2.isFile() ? { size: info2.size } : {} });
@@ -29382,15 +29998,15 @@ var CodexWorkspaceBridge = class {
29382
29998
  }
29383
29999
  }
29384
30000
  async safePath(root, path, directory) {
29385
- const candidate = isAbsolute4(path) ? resolve3(path) : resolve3(root, path);
29386
- const rel = relative2(root, candidate);
29387
- if (rel.startsWith("..") || isAbsolute4(rel)) throw new RpcError("CODEX_WORKSPACE_PATH_DENIED", "The requested workspace path is outside the CodeX workspace.");
30001
+ const candidate = isAbsolute5(path) ? resolve4(path) : resolve4(root, path);
30002
+ const rel = relative3(root, candidate);
30003
+ if (rel.startsWith("..") || isAbsolute5(rel)) throw new RpcError("CODEX_WORKSPACE_PATH_DENIED", "The requested workspace path is outside the CodeX workspace.");
29388
30004
  try {
29389
30005
  const info = await lstat(candidate);
29390
30006
  if (info.isSymbolicLink()) throw new Error();
29391
30007
  const canonical = await realpath2(candidate);
29392
- const canonicalRel = relative2(root, canonical);
29393
- if (canonicalRel.startsWith("..") || isAbsolute4(canonicalRel)) throw new Error();
30008
+ const canonicalRel = relative3(root, canonical);
30009
+ if (canonicalRel.startsWith("..") || isAbsolute5(canonicalRel)) throw new Error();
29394
30010
  if (directory && !info.isDirectory()) throw new Error();
29395
30011
  return canonical;
29396
30012
  } catch {
@@ -29433,14 +30049,14 @@ function pipeTerminalSpawner() {
29433
30049
  child.stdout.on("data", (data2) => queue.push(Buffer.from(data2).toString("utf8")));
29434
30050
  child.stderr.on("data", (data2) => queue.push(Buffer.from(data2).toString("utf8")));
29435
30051
  child.on("error", () => queue.end());
29436
- const completed = new Promise((resolve4) => {
30052
+ const completed = new Promise((resolve5) => {
29437
30053
  child.on("error", () => {
29438
30054
  queue.end();
29439
- resolve4({ exitCode: null });
30055
+ resolve5({ exitCode: null });
29440
30056
  });
29441
30057
  child.on("exit", (code) => {
29442
30058
  queue.end();
29443
- resolve4({ exitCode: code });
30059
+ resolve5({ exitCode: code });
29444
30060
  });
29445
30061
  });
29446
30062
  return {
@@ -29531,7 +30147,7 @@ function readLimit(args) {
29531
30147
 
29532
30148
  // src/service.ts
29533
30149
  var HostPluginRuntime = class {
29534
- constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost, terminalSpawner) {
30150
+ constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost, terminalSpawner, sessionSource) {
29535
30151
  this.config = config;
29536
30152
  this.identities = identities;
29537
30153
  this.apiProxy = apiProxy;
@@ -29539,6 +30155,7 @@ var HostPluginRuntime = class {
29539
30155
  this.localGateway = localGateway;
29540
30156
  this.fileViewerHost = fileViewerHost;
29541
30157
  this.terminalSpawner = terminalSpawner;
30158
+ this.sessionSource = sessionSource;
29542
30159
  this.terminalEnabled = config.terminal.enabled;
29543
30160
  this.loopbackPorts = [...config.loopback.ports];
29544
30161
  this.paused = config.hostControl?.paused === true;
@@ -29563,7 +30180,8 @@ var HostPluginRuntime = class {
29563
30180
  () => this.terminalEnabled,
29564
30181
  this.codexWorkspaceState,
29565
30182
  this.terminalSpawner
29566
- )
30183
+ ),
30184
+ this.sessionSource === void 0 ? void 0 : (sessionId) => this.sessionSource?.()?.get?.(sessionId)?.header?.cwd
29567
30185
  ) : void 0;
29568
30186
  const fileViewer = new RemoteFileViewerBridge(
29569
30187
  () => this.fileViewerHost?.(),
@@ -29985,7 +30603,7 @@ function isPlainRecord(value) {
29985
30603
  // src/cli.ts
29986
30604
  import { stat as stat7 } from "node:fs/promises";
29987
30605
  import { hostname as hostname3 } from "node:os";
29988
- import { join as join9 } from "node:path";
30606
+ import { join as join12 } from "node:path";
29989
30607
  var QR_POLL_INTERVAL_MS = 2e3;
29990
30608
  var TERMINAL_QR_MARGIN = 4;
29991
30609
  async function runCli(args = process.argv.slice(2), dependencies = {}) {
@@ -30073,7 +30691,7 @@ async function status(args, runtime) {
30073
30691
  `Server: ${serverUrl}`,
30074
30692
  "Host control: enabled (dsh-TUI default)"
30075
30693
  ];
30076
- if (!await exists3(join9(directory, "device.json"))) {
30694
+ if (!await exists3(join12(directory, "device.json"))) {
30077
30695
  lines.push("Device: not initialized", "Authorization: logged out", "Credential: unavailable");
30078
30696
  write(runtime.stdout, `${lines.join("\n")}
30079
30697
 
@@ -30117,7 +30735,7 @@ async function logout(args, runtime) {
30117
30735
  const serverUrl = selectedServer();
30118
30736
  const root = new IdentityStore({ env: runtime.env }).directory;
30119
30737
  const directory = serverStorageDirectory(root, serverUrl, "host");
30120
- if (!await exists3(join9(directory, "device.json"))) {
30738
+ if (!await exists3(join12(directory, "device.json"))) {
30121
30739
  await new ServerCredentialStore(directory).clear();
30122
30740
  write(runtime.stdout, "This Host is already logged out.\n");
30123
30741
  return 0;
@@ -30148,7 +30766,7 @@ function resolveDependencies(input2) {
30148
30766
  stdout: input2.stdout ?? process.stdout,
30149
30767
  stderr: input2.stderr ?? process.stderr,
30150
30768
  now: input2.now ?? Date.now,
30151
- wait: input2.wait ?? ((milliseconds) => new Promise((resolve4) => setTimeout(resolve4, milliseconds))),
30769
+ wait: input2.wait ?? ((milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds))),
30152
30770
  renderQr: input2.renderQr ?? renderTerminalQr,
30153
30771
  createIdentityStore: input2.createIdentityStore ?? ((options) => new IdentityStore(options)),
30154
30772
  createHostApi: input2.createHostApi ?? ((serverUrl, store) => new HostServerApi(serverUrl, store))
@@ -30597,7 +31215,7 @@ function errorCode6(error) {
30597
31215
  return "CONNECTION_FAILED";
30598
31216
  }
30599
31217
  function wait(milliseconds) {
30600
- return new Promise((resolve4) => setTimeout(resolve4, milliseconds));
31218
+ return new Promise((resolve5) => setTimeout(resolve5, milliseconds));
30601
31219
  }
30602
31220
 
30603
31221
  // src/remote-file-content-provider.ts
@@ -30782,6 +31400,7 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
30782
31400
  const nativeTypertGateway = ctx.get("typertGateway");
30783
31401
  const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway, { peer: peerFor }).local();
30784
31402
  const subprocess = ctx.get("subprocess", false);
31403
+ const sessionSource = () => ctx.get("sessions", false);
30785
31404
  const deepseekAccount = ctx.get("deepseekAccount", false);
30786
31405
  const deepseekSession = deepseekAccount === void 0 ? void 0 : {
30787
31406
  read: async () => {
@@ -30813,7 +31432,8 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
30813
31432
  logger,
30814
31433
  localTypertGateway,
30815
31434
  () => ctx.get("fileViewerHost"),
30816
- subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess)
31435
+ subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess),
31436
+ sessionSource
30817
31437
  );
30818
31438
  let clientRuntime;
30819
31439
  const hostControl = runtime;