ds-harness-remote 0.4.9 → 0.4.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +9 -4
  2. package/dist/client-runtime.d.ts +3 -0
  3. package/dist/client-runtime.d.ts.map +1 -1
  4. package/dist/client-runtime.js +46 -14
  5. package/dist/client-runtime.js.map +1 -1
  6. package/dist/client.github.js +1 -1
  7. package/dist/client.js +1 -1
  8. package/dist/client.js.map +1 -1
  9. package/dist/codex/domain.d.ts +4 -0
  10. package/dist/codex/domain.d.ts.map +1 -1
  11. package/dist/codex/domain.js +74 -1
  12. package/dist/codex/domain.js.map +1 -1
  13. package/dist/codex/method-policy.d.ts +2 -2
  14. package/dist/codex/permissions.d.ts +4 -0
  15. package/dist/codex/permissions.d.ts.map +1 -0
  16. package/dist/codex/permissions.js +21 -0
  17. package/dist/codex/permissions.js.map +1 -0
  18. package/dist/codex/virtual-harness.d.ts +14 -4
  19. package/dist/codex/virtual-harness.d.ts.map +1 -1
  20. package/dist/codex/virtual-harness.js +180 -73
  21. package/dist/codex/virtual-harness.js.map +1 -1
  22. package/dist/harness-version.d.ts +7 -0
  23. package/dist/harness-version.d.ts.map +1 -1
  24. package/dist/harness-version.js +16 -0
  25. package/dist/harness-version.js.map +1 -1
  26. package/dist/index.js +648 -319
  27. package/dist/index.js.map +4 -4
  28. package/dist/remote-api-proxy.d.ts.map +1 -1
  29. package/dist/remote-api-proxy.js +34 -1
  30. package/dist/remote-api-proxy.js.map +1 -1
  31. package/dist/server-api.d.ts.map +1 -1
  32. package/dist/server-api.js +4 -5
  33. package/dist/server-api.js.map +1 -1
  34. package/dist/service.d.ts +1 -0
  35. package/dist/service.d.ts.map +1 -1
  36. package/dist/service.js +8 -4
  37. package/dist/service.js.map +1 -1
  38. package/dist/version.d.ts +1 -1
  39. package/dist/version.d.ts.map +1 -1
  40. package/dist/version.js +1 -1
  41. package/dist/version.js.map +1 -1
  42. package/dsh-plugin.json +1 -1
  43. package/package.json +21 -21
package/dist/index.js CHANGED
@@ -4055,6 +4055,10 @@ var HARNESS_API_TRANSFER_CHUNK_BYTES = 512 * 1024;
4055
4055
  var CODEX_APP_TRANSFER_CHUNK_BYTES = 512 * 1024;
4056
4056
  var MAX_HARNESS_API_TRANSFER_BYTES = 288 * 1024 * 1024;
4057
4057
  var MAX_CODEX_APP_TRANSFER_BYTES = 288 * 1024 * 1024;
4058
+ var MAX_DEVICE_NAME_LENGTH = 128;
4059
+ var MAX_DEVICE_PLATFORM_LENGTH = 64;
4060
+ var MAX_DEVICE_VERSION_LENGTH = 64;
4061
+ var MAX_AUTH_TOKEN_LENGTH = 8 * 1024;
4058
4062
  var SECURE_FRAGMENT_MAGIC = new Uint8Array([68, 83, 72, 70]);
4059
4063
  var SECURE_FRAGMENT_VERSION = 1;
4060
4064
  var SECURE_FRAGMENT_HEADER_BYTES = 17;
@@ -4138,6 +4142,73 @@ var messageTypeSchema = external_exports.enum(messageTypes);
4138
4142
  var controlFrameTypeSchema = external_exports.enum(controlFrameTypes);
4139
4143
  var uniqueStrings = (values) => new Set(values).size === values.length;
4140
4144
  var uniqueNumbers = (values) => new Set(values).size === values.length;
4145
+ var utf8Length = (value) => new TextEncoder().encode(value).byteLength;
4146
+ var boundedUtf8Schema = (minimum, maximum) => external_exports.string().refine((value) => utf8Length(value) >= minimum && utf8Length(value) <= maximum);
4147
+ var deviceIdSchema = external_exports.string().refine((value) => /^[0-9A-HJKMNP-TV-Z]{26}$/iu.test(value) || /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value), "Device ID must be a ULID or UUID.");
4148
+ var deviceNameSchema = boundedUtf8Schema(1, MAX_DEVICE_NAME_LENGTH).refine((value) => value.trim().length > 0, "Device name must contain visible text.");
4149
+ var devicePlatformSchema = external_exports.string().min(1).max(MAX_DEVICE_PLATFORM_LENGTH).regex(/^[a-z0-9][a-z0-9._-]*$/u);
4150
+ var identityKeySchema = external_exports.string().regex(/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/u);
4151
+ var deviceVersionSchema = boundedUtf8Schema(1, MAX_DEVICE_VERSION_LENGTH);
4152
+ var authTokenSchema = boundedUtf8Schema(16, MAX_AUTH_TOKEN_LENGTH);
4153
+ var expiresAtSchema = external_exports.number().int().positive().safe();
4154
+ var hostDeviceDescriptorSchema = external_exports.object({
4155
+ deviceId: deviceIdSchema,
4156
+ name: deviceNameSchema,
4157
+ role: external_exports.literal("host"),
4158
+ platform: devicePlatformSchema,
4159
+ identityKey: identityKeySchema,
4160
+ clientVersion: deviceVersionSchema,
4161
+ harnessVersion: deviceVersionSchema.optional()
4162
+ }).strict();
4163
+ var clientDeviceDescriptorSchema = external_exports.object({
4164
+ deviceId: deviceIdSchema,
4165
+ name: deviceNameSchema,
4166
+ role: external_exports.literal("client"),
4167
+ platform: devicePlatformSchema,
4168
+ identityKey: identityKeySchema,
4169
+ clientVersion: deviceVersionSchema
4170
+ }).strict();
4171
+ var accountDeviceDescriptorSchema = external_exports.union([
4172
+ hostDeviceDescriptorSchema,
4173
+ clientDeviceDescriptorSchema
4174
+ ]);
4175
+ var deviceRegistrationRequestSchema = external_exports.object({
4176
+ v: external_exports.literal(PROTOCOL_VERSION),
4177
+ device: accountDeviceDescriptorSchema
4178
+ }).strict();
4179
+ var hostRegistrationCodeRequestSchema = external_exports.object({
4180
+ v: external_exports.literal(PROTOCOL_VERSION),
4181
+ code: external_exports.string().regex(/^[A-Z0-9]{4}-[A-Z0-9]{4}$/u),
4182
+ device: hostDeviceDescriptorSchema
4183
+ }).strict();
4184
+ var deviceRefreshRequestSchema = external_exports.object({
4185
+ deviceId: deviceIdSchema,
4186
+ refreshToken: authTokenSchema
4187
+ }).strict();
4188
+ var deviceTokenPairSchema = external_exports.object({
4189
+ accessToken: authTokenSchema,
4190
+ accessTokenExpiresAt: expiresAtSchema,
4191
+ refreshToken: authTokenSchema,
4192
+ refreshTokenExpiresAt: expiresAtSchema
4193
+ }).strict();
4194
+ var browserAuthorizationExchangeRequestSchema = external_exports.object({
4195
+ v: external_exports.literal(PROTOCOL_VERSION),
4196
+ device: external_exports.object({
4197
+ deviceId: deviceIdSchema,
4198
+ name: deviceNameSchema,
4199
+ role: external_exports.literal("client"),
4200
+ platform: external_exports.literal("browser"),
4201
+ identityKey: identityKeySchema,
4202
+ clientVersion: deviceVersionSchema
4203
+ }).strict()
4204
+ }).strict();
4205
+ var browserAuthorizationExchangeResponseSchema = external_exports.object({
4206
+ accessToken: authTokenSchema,
4207
+ accessTokenExpiresAt: expiresAtSchema,
4208
+ refreshToken: authTokenSchema,
4209
+ refreshTokenExpiresAt: expiresAtSchema,
4210
+ account: boundedUtf8Schema(1, 254).refine((value) => value.trim().length > 0)
4211
+ }).strict();
4141
4212
  var remoteMessageSchema = external_exports.object({
4142
4213
  v: external_exports.literal(PROTOCOL_VERSION),
4143
4214
  id: external_exports.string().min(1),
@@ -13906,8 +13977,51 @@ function uuidV7(now = Date.now()) {
13906
13977
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
13907
13978
  }
13908
13979
 
13980
+ // src/harness-version.ts
13981
+ import { readFile } from "node:fs/promises";
13982
+ import { dirname, isAbsolute, join } from "node:path";
13983
+ var LEGACY_PLACEHOLDER_VERSION = "0.0.1";
13984
+ function normalizeHarnessVersion(value) {
13985
+ if (typeof value !== "string") return void 0;
13986
+ const version = value.trim();
13987
+ if (version.length === 0 || version.length > 64 || /[\u0000-\u001f]/u.test(version)) return void 0;
13988
+ return version;
13989
+ }
13990
+ function selectHarnessVersion(reportedVersion, distributionVersion) {
13991
+ if (reportedVersion !== void 0 && reportedVersion !== LEGACY_PLACEHOLDER_VERSION) return reportedVersion;
13992
+ return distributionVersion;
13993
+ }
13994
+ function harnessSessionGeneration(version) {
13995
+ if (version === void 0) return "legacy";
13996
+ const match = /^(?:dsh-)?v?(\d+)\.(\d+)\.(\d+)(?:-|$)/u.exec(version.trim());
13997
+ if (match === null) return "legacy";
13998
+ const major = Number(match[1]);
13999
+ const minor = Number(match[2]);
14000
+ const patch = Number(match[3]);
14001
+ return major === 0 && minor === 1 && patch >= 5 ? "v3" : "legacy";
14002
+ }
14003
+ async function readHarnessDistributionVersion(entrypoint = process.argv[1]) {
14004
+ if (entrypoint === void 0 || !isAbsolute(entrypoint)) return void 0;
14005
+ let directory = dirname(entrypoint);
14006
+ for (let depth = 0; depth < 8; depth += 1) {
14007
+ try {
14008
+ const manifest = JSON.parse(await readFile(join(directory, "package.json"), "utf8"));
14009
+ if (manifest.name === "@deepseek-ai/dsh") return normalizeHarnessVersion(manifest.version);
14010
+ } catch {
14011
+ }
14012
+ const parent = dirname(directory);
14013
+ if (parent === directory) break;
14014
+ directory = parent;
14015
+ }
14016
+ return void 0;
14017
+ }
14018
+
13909
14019
  // src/remote-api-proxy.ts
13910
14020
  var DIRECT_API_CALL_BYTES = 2 * 1024 * 1024;
14021
+ var AGENT_PRESET_SETTINGS_NS = "agent-presets";
14022
+ var LEGACY_AGENT_PRESET_ALIASES = {
14023
+ code: "ptc"
14024
+ };
13911
14025
  var RemoteHarnessApiProxy = class {
13912
14026
  constructor(client) {
13913
14027
  this.client = client;
@@ -13996,7 +14110,7 @@ var RemoteHarnessApiProxy = class {
13996
14110
  const params = {
13997
14111
  method,
13998
14112
  rpcId: String(request.rpcId),
13999
- payload: request.payload
14113
+ payload: normalizeLegacyRequest(method, request.payload)
14000
14114
  };
14001
14115
  const encoded = new TextEncoder().encode(JSON.stringify(params));
14002
14116
  const response = method === "session.attachment" || encoded.byteLength > DIRECT_API_CALL_BYTES ? await this.callTransferred(encoded, signal) : await this.client.rpc("harness.api.call", params, signal);
@@ -14108,9 +14222,32 @@ function normalizeLegacyResponse(method, response) {
14108
14222
  }
14109
14223
  };
14110
14224
  }
14225
+ function normalizeLegacyRequest(method, payload) {
14226
+ if (!isRecord2(payload)) return payload;
14227
+ if (method === "agentPreset.read") return replaceAgentPreset(payload, "agentPreset");
14228
+ if (method === "agentPreset.select") return replaceAgentPreset(payload, "agentPreset");
14229
+ if (method === "agentPreset.copy") return replaceAgentPreset(payload, "from");
14230
+ if (method === "settings.update" || method === "settings.replace" || method === "settings.mutate") {
14231
+ return replaceAgentPresetSettings(payload);
14232
+ }
14233
+ return payload;
14234
+ }
14235
+ function replaceAgentPreset(payload, key) {
14236
+ const value = payload[key];
14237
+ const replacement = typeof value === "string" ? LEGACY_AGENT_PRESET_ALIASES[value] : void 0;
14238
+ return replacement === void 0 ? payload : { ...payload, [key]: replacement };
14239
+ }
14240
+ function replaceAgentPresetSettings(payload) {
14241
+ if (payload.ns !== AGENT_PRESET_SETTINGS_NS || !isRecord2(payload.patch)) return payload;
14242
+ const patch = replaceAgentPreset(payload.patch, "default");
14243
+ return patch === payload.patch ? payload : { ...payload, patch };
14244
+ }
14111
14245
  function isRemoteDisconnect(error) {
14112
14246
  return error instanceof RemoteClientError && (error.code === "TRANSPORT_CLOSED" || error.code === "CLIENT_CLOSED");
14113
14247
  }
14248
+ function isRecord2(value) {
14249
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14250
+ }
14114
14251
  var AsyncFrameQueue = class {
14115
14252
  values = [];
14116
14253
  waiters = [];
@@ -14345,16 +14482,16 @@ function routeStreamEvent2(event, streamId, queue) {
14345
14482
  queue.fail(remoteFailure({
14346
14483
  code: typeof failure2?.code === "string" ? failure2.code : "internal",
14347
14484
  message: typeof failure2?.message === "string" ? failure2.message : "The remote Harness stream failed.",
14348
- details: isRecord2(failure2?.details) ? failure2.details : {}
14485
+ details: isRecord3(failure2?.details) ? failure2.details : {}
14349
14486
  }));
14350
14487
  } else {
14351
14488
  queue.close();
14352
14489
  }
14353
14490
  }
14354
14491
  function parseRpcResult(value) {
14355
- if (!isRecord2(value)) throw new Error("The remote Host returned an invalid Gateway result.");
14492
+ if (!isRecord3(value)) throw new Error("The remote Host returned an invalid Gateway result.");
14356
14493
  if (value.ok === true) return Object.hasOwn(value, "value") ? { ok: true, value: value.value } : { ok: true };
14357
- if (value.ok !== false || !isRecord2(value.error) || typeof value.error.code !== "string" || typeof value.error.message !== "string" || !isRecord2(value.error.details)) {
14494
+ if (value.ok !== false || !isRecord3(value.error) || typeof value.error.code !== "string" || typeof value.error.message !== "string" || !isRecord3(value.error.details)) {
14358
14495
  throw new Error("The remote Host returned an invalid Gateway failure.");
14359
14496
  }
14360
14497
  return {
@@ -14388,13 +14525,32 @@ function base64ToBytes3(value) {
14388
14525
  if (bytesToBase643(bytes) !== value) throw new Error("The remote Host returned non-canonical Harness Remote transfer data.");
14389
14526
  return bytes;
14390
14527
  }
14391
- function isRecord2(value) {
14528
+ function isRecord3(value) {
14392
14529
  return typeof value === "object" && value !== null && !Array.isArray(value);
14393
14530
  }
14394
14531
  function hasErrorCode(error, code) {
14395
14532
  return error instanceof Error && "code" in error && error.code === code;
14396
14533
  }
14397
14534
 
14535
+ // src/codex/permissions.ts
14536
+ function codexPermissionPresetFromResponse(value) {
14537
+ const root = record(value);
14538
+ for (const settings of [root, record(root.threadSettings)]) {
14539
+ const sandbox = settings.sandbox ?? settings.sandboxPolicy;
14540
+ const type = record(sandbox).type;
14541
+ if (settings.approvalPolicy === "never" && (sandbox === "danger-full-access" || type === "dangerFullAccess")) {
14542
+ return "danger-full-access";
14543
+ }
14544
+ if (settings.approvalPolicy === "on-request" && (sandbox === "workspace-write" || type === "workspaceWrite")) {
14545
+ return "workspace-write";
14546
+ }
14547
+ }
14548
+ return void 0;
14549
+ }
14550
+ function record(value) {
14551
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
14552
+ }
14553
+
14398
14554
  // src/codex/virtual-harness.ts
14399
14555
  var CODEX_SESSION_PREFIX = "codex:";
14400
14556
  var CODEX_WORKSPACE_PREFIX = "codex-workspace:";
@@ -14420,9 +14576,10 @@ async function discoverCodexVirtualWorkspaces(client, signal) {
14420
14576
  return (await loadCatalog(client, signal)).workspaces;
14421
14577
  }
14422
14578
  var CodexVirtualHarness = class _CodexVirtualHarness {
14423
- constructor(client, host) {
14579
+ constructor(client, host, sessionGeneration = "legacy") {
14424
14580
  this.client = client;
14425
14581
  this.host = host;
14582
+ this.sessionGeneration = sessionGeneration;
14426
14583
  this.api = this.createApiProxy();
14427
14584
  }
14428
14585
  api;
@@ -14446,8 +14603,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14446
14603
  commandSeq = 0;
14447
14604
  selectedWorkspaceId;
14448
14605
  closed = false;
14449
- static remote(core, host) {
14450
- return new _CodexVirtualHarness(new CodexRemoteClient(core), host);
14606
+ static remote(core, host, sessionGeneration = "legacy") {
14607
+ return new _CodexVirtualHarness(new CodexRemoteClient(core), host, sessionGeneration);
14451
14608
  }
14452
14609
  async workspaces(signal) {
14453
14610
  return (await this.refreshCatalog(signal)).workspaces;
@@ -14643,10 +14800,11 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14643
14800
  return this.selectedModels.get(sessionId) ?? directory?.default ?? modelSelection();
14644
14801
  }
14645
14802
  permissionSelection(sessionId) {
14646
- return this.selectedPermissions.get(sessionId) ?? CODEX_DEFAULT_PERMISSION;
14803
+ return this.selectedPermissions.get(sessionId);
14647
14804
  }
14648
14805
  setPermissionSelection(sessionId, preset) {
14649
- this.selectedPermissions.set(sessionId, preset);
14806
+ if (preset === void 0) this.selectedPermissions.delete(sessionId);
14807
+ else this.selectedPermissions.set(sessionId, preset);
14650
14808
  this.publishProjection(sessionId, "permissions", codexPermissionsProjection(preset), this.nextProjectionSeq());
14651
14809
  }
14652
14810
  commandList(args) {
@@ -14670,17 +14828,22 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14670
14828
  result: { kind: "error", text: "CodeX Remote permission commands do not accept attachments." }
14671
14829
  };
14672
14830
  const requested = (match[1] ?? "").trim();
14673
- if (requested === "") return {
14674
- commandId,
14675
- result: { kind: "success", text: `current preset ${this.permissionSelection(sessionId)}` }
14676
- };
14831
+ if (requested === "") {
14832
+ await this.readHistoryPage(threadId, { maxMessages: 1 }, signal);
14833
+ return {
14834
+ commandId,
14835
+ result: { kind: "success", text: `current preset ${this.permissionSelection(sessionId) ?? "Host settings (not reported)"}` }
14836
+ };
14837
+ }
14677
14838
  if (!isCodexPermissionPreset(requested)) return {
14678
14839
  commandId,
14679
14840
  result: { kind: "error", text: `unknown preset "${requested}" (available: workspace-write, danger-full-access)` }
14680
14841
  };
14681
14842
  const result = await this.client.request("thread/resume", { threadId, permissionPreset: requested }, signal);
14682
- const effective = codexPermissionPresetFromResponse(result) ?? requested;
14843
+ const effective = codexPermissionPresetFromResponse(result);
14844
+ if (effective === void 0) throw new Error("The Host did not confirm the CodeX permission preset.");
14683
14845
  this.setPermissionSelection(sessionId, effective);
14846
+ if (effective !== requested) throw new Error("The Host did not apply the requested CodeX permission preset.");
14684
14847
  return { commandId, result: { kind: "success", text: `preset ${effective}` } };
14685
14848
  }
14686
14849
  sessionTitle(sessionId) {
@@ -14812,7 +14975,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14812
14975
  return queue.iterate(() => this.eventStreams.delete(clientId));
14813
14976
  }
14814
14977
  async sessionFollow(request, signal) {
14815
- const sessionId = sessionIdFromAddress(record(request.address));
14978
+ const sessionId = sessionIdFromAddress(record2(request.address));
14816
14979
  const threadId = nativeThreadId(sessionId);
14817
14980
  const history = await this.readHistoryPage(threadId, {
14818
14981
  maxMessages: optionalPositiveInteger(request.maxMessages)
@@ -14831,6 +14994,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14831
14994
  streamedBlocks: /* @__PURE__ */ new Map(),
14832
14995
  nextBlockIndex: 0,
14833
14996
  streamActive: false,
14997
+ assistantStreamRevision: 0,
14834
14998
  liveItems: /* @__PURE__ */ new Map(),
14835
14999
  liveToolOutput: /* @__PURE__ */ new Map(),
14836
15000
  liveToolResultSeq: /* @__PURE__ */ new Map(),
@@ -14854,7 +15018,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14854
15018
  permissions: codexPermissionsProjection(this.permissionSelection(sessionId)),
14855
15019
  imageLimits: codexImageLimitsProjection()
14856
15020
  }
14857
- }
15021
+ },
15022
+ ...this.sessionGeneration === "v3" ? { assistantStream: { revision: 0 } } : {}
14858
15023
  });
14859
15024
  try {
14860
15025
  const stream = await this.client.subscribe(
@@ -14875,7 +15040,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14875
15040
  });
14876
15041
  }
14877
15042
  acceptCodexFrame(follow, frame) {
14878
- const params = record(frame.params);
15043
+ const params = record2(frame.params);
14879
15044
  if (frame.method === "thread/name/updated") {
14880
15045
  const name2 = string(params.threadName);
14881
15046
  if (name2 === void 0) return;
@@ -14887,11 +15052,12 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14887
15052
  }
14888
15053
  if (frame.method === "thread/settings/updated") {
14889
15054
  const preset = codexPermissionPresetFromResponse(params);
14890
- if (preset !== void 0) this.setPermissionSelection(follow.sessionId, preset);
15055
+ this.setPermissionSelection(follow.sessionId, preset);
14891
15056
  return;
14892
15057
  }
14893
15058
  if (frame.method === "turn/started") {
14894
- const turn = record(params.turn);
15059
+ const turn = record2(params.turn);
15060
+ this.endAssistantAttempt(follow, { kind: "abandoned" });
14895
15061
  this.resetLiveTurn(follow);
14896
15062
  follow.turn += 1;
14897
15063
  follow.activeTurnId = string(turn.id);
@@ -14900,11 +15066,11 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14900
15066
  this.pushEvent(follow, "turn/start", { turn: follow.turn });
14901
15067
  this.pushEvent(follow, "step/start", { turn: follow.turn, step: 1 });
14902
15068
  this.emitRemoteEvent("api-session/status", [follow.sessionId, true]);
14903
- for (const item of array(turn.items)) this.acceptCompletedItem(follow, record(item), false);
15069
+ for (const item of array(turn.items)) this.acceptCompletedItem(follow, record2(item), false);
14904
15070
  return;
14905
15071
  }
14906
15072
  if (frame.method === "item/started" || frame.method === "item/completed") {
14907
- const item = record(params.item);
15073
+ const item = record2(params.item);
14908
15074
  const itemId = string(item.id);
14909
15075
  if (frame.method === "item/started" && itemId !== void 0) {
14910
15076
  follow.startedItems.add(itemId);
@@ -14941,7 +15107,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14941
15107
  }
14942
15108
  if (frame.method === "turn/plan/updated") {
14943
15109
  const todos = array(params.plan).flatMap((value) => {
14944
- const item = record(value);
15110
+ const item = record2(value);
14945
15111
  const content = string(item.step);
14946
15112
  if (content === void 0) return [];
14947
15113
  const status2 = item.status === "inProgress" ? "in_progress" : item.status === "completed" ? "completed" : "pending";
@@ -14975,7 +15141,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14975
15141
  return;
14976
15142
  }
14977
15143
  if (frame.method === "thread/status/changed") {
14978
- const status2 = record(params.status);
15144
+ const status2 = record2(params.status);
14979
15145
  const running = status2.type === "active";
14980
15146
  this.emitRemoteEvent("api-session/status", [follow.sessionId, running]);
14981
15147
  if (status2.type === "systemError") {
@@ -15002,11 +15168,12 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15002
15168
  return;
15003
15169
  }
15004
15170
  if (frame.method === "turn/completed") {
15005
- const turn = record(params.turn);
15006
- for (const item of array(turn.items)) this.acceptCompletedItem(follow, record(item), true);
15171
+ const turn = record2(params.turn);
15172
+ for (const item of array(turn.items)) this.acceptCompletedItem(follow, record2(item), true);
15007
15173
  if (follow.stepOpen) {
15008
15174
  this.closeAllStreamBlocks(follow);
15009
15175
  if (follow.streamActive) this.finishStream(follow);
15176
+ this.endAssistantAttempt(follow, { kind: "abandoned" });
15010
15177
  this.pushEvent(follow, "step/end", { turn: follow.turn, step: 1 });
15011
15178
  follow.stepOpen = false;
15012
15179
  }
@@ -15045,6 +15212,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15045
15212
  this.closeItemStreamBlocks(follow, itemId, itemText2(item));
15046
15213
  if (type === "agentMessage" && follow.streamActive && follow.streamedBlocks.size === 0) this.finishStream(follow);
15047
15214
  const supplementalImages = type === "agentMessage" ? follow.pendingToolImages : [];
15215
+ let assistantMessageSeq;
15048
15216
  for (const event of itemEvents(
15049
15217
  item,
15050
15218
  follow.turn,
@@ -15056,7 +15224,17 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15056
15224
  )) {
15057
15225
  if (event.type === "tool/call" && follow.startedItems.has(itemId)) continue;
15058
15226
  if (event.type === "tool/result") this.pushToolResult(follow, itemId, event);
15059
- else this.pushEvent(follow, event.type, event.data, event.view);
15227
+ else {
15228
+ const seq = this.pushEvent(follow, event.type, event.data, event.view);
15229
+ if (event.type === "assistant/message") assistantMessageSeq = seq;
15230
+ }
15231
+ }
15232
+ if (type === "agentMessage" && assistantMessageSeq !== void 0) {
15233
+ this.endAssistantAttempt(follow, {
15234
+ kind: "committed",
15235
+ eventType: "assistant/message",
15236
+ seq: assistantMessageSeq
15237
+ });
15060
15238
  }
15061
15239
  if (type === "agentMessage" && supplementalImages.length > 0) follow.pendingToolImages = [];
15062
15240
  else if (type !== void 0 && isToolItemType2(type)) {
@@ -15073,18 +15251,21 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15073
15251
  type,
15074
15252
  seq,
15075
15253
  time: Date.now(),
15076
- data,
15254
+ data: adaptEventData(
15255
+ type,
15256
+ type === "assistant/message" && this.sessionGeneration === "v3" && follow.assistantAttempt !== void 0 ? { ...record2(data), stream: follow.assistantAttempt.stream } : data,
15257
+ this.sessionGeneration
15258
+ ),
15077
15259
  ...isSurfaceEvent(type) ? placement === void 0 ? { surfaceOp: "append" } : {
15078
- surfaceOp: { op: "replace", start: placement.replaceSeq, end: placement.replaceSeq },
15260
+ surfaceOp: this.sessionGeneration === "v3" ? { op: "replace", startSeq: placement.replaceSeq, endSeq: placement.replaceSeq } : { op: "replace", start: placement.replaceSeq, end: placement.replaceSeq },
15079
15261
  sourceEventSeqs: [placement.replaceSeq]
15080
15262
  } : {}
15081
15263
  };
15082
15264
  this.cacheImageBlocks(follow.sessionId, event.data);
15083
15265
  if (!follow.rcOnly) {
15084
- const alphaEvent = typeof event.surfaceOp === "object" ? { ...event, surfaceOp: "replace" } : event;
15085
15266
  follow.queue.push({
15086
15267
  type: "event",
15087
- event: alphaEvent,
15268
+ event,
15088
15269
  ...view === void 0 ? {} : { view }
15089
15270
  });
15090
15271
  }
@@ -15098,7 +15279,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15098
15279
  }
15099
15280
  cacheImageBlocks(sessionId, value) {
15100
15281
  for (const block of collectImageBlocks(value)) {
15101
- const attachment = record(block.attachment);
15282
+ const attachment = record2(block.attachment);
15102
15283
  const attachmentId = string(attachment.attachmentId);
15103
15284
  const data = string(block.data);
15104
15285
  if (attachmentId === void 0 || data === void 0 || !attachmentId.startsWith(CODEX_IMAGE_ATTACHMENT_PREFIX)) continue;
@@ -15111,52 +15292,105 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15111
15292
  const block = { itemId, index: follow.nextBlockIndex++, kind, text: "" };
15112
15293
  follow.streamedBlocks.set(key, block);
15113
15294
  follow.streamActive = true;
15114
- this.pushEvent(follow, "assistant/chunk", {
15115
- turn: follow.turn,
15116
- step: 1,
15117
- chunk: { type: "block-start", index: block.index, blockType: kind }
15118
- });
15295
+ this.pushAssistantChunk(follow, { type: "block-start", index: block.index, blockType: kind });
15119
15296
  return block;
15120
15297
  }
15121
15298
  appendStreamDelta(follow, key, itemId, kind, delta) {
15122
15299
  const block = this.ensureStreamBlock(follow, key, itemId, kind);
15123
15300
  block.text = appendBoundedText(block.text, delta);
15124
- this.pushEvent(follow, "assistant/chunk", {
15125
- turn: follow.turn,
15126
- step: 1,
15127
- chunk: { type: kind === "reasoning" ? "reasoning-delta" : "text-delta", index: block.index, text: delta }
15301
+ this.pushAssistantChunk(follow, {
15302
+ type: kind === "reasoning" ? "reasoning-delta" : "text-delta",
15303
+ index: block.index,
15304
+ text: delta
15128
15305
  });
15129
15306
  }
15130
15307
  closeItemStreamBlocks(follow, itemId, fallback) {
15131
15308
  const matches = [...follow.streamedBlocks].filter(([, block]) => block.itemId === itemId);
15132
15309
  for (const [key, block] of matches) {
15133
15310
  const text = block.text || fallback || "";
15134
- this.pushEvent(follow, "assistant/chunk", {
15135
- turn: follow.turn,
15136
- step: 1,
15137
- chunk: { type: "block-end", index: block.index, block: { type: block.kind, text } }
15311
+ this.pushAssistantChunk(follow, {
15312
+ type: "block-end",
15313
+ index: block.index,
15314
+ block: { type: block.kind, text }
15138
15315
  });
15139
15316
  follow.streamedBlocks.delete(key);
15140
15317
  }
15141
15318
  }
15142
15319
  closeAllStreamBlocks(follow) {
15143
15320
  for (const block of follow.streamedBlocks.values()) {
15144
- this.pushEvent(follow, "assistant/chunk", {
15145
- turn: follow.turn,
15146
- step: 1,
15147
- chunk: { type: "block-end", index: block.index, block: { type: block.kind, text: block.text } }
15321
+ this.pushAssistantChunk(follow, {
15322
+ type: "block-end",
15323
+ index: block.index,
15324
+ block: { type: block.kind, text: block.text }
15148
15325
  });
15149
15326
  }
15150
15327
  follow.streamedBlocks.clear();
15151
15328
  }
15152
15329
  finishStream(follow) {
15153
- this.pushEvent(follow, "assistant/chunk", {
15154
- turn: follow.turn,
15155
- step: 1,
15156
- chunk: { type: "finish", reason: { kind: "stop" } }
15157
- });
15330
+ this.pushAssistantChunk(follow, { type: "finish", reason: { kind: "stop" } });
15158
15331
  follow.streamActive = false;
15159
15332
  }
15333
+ pushAssistantChunk(follow, chunk) {
15334
+ if (this.sessionGeneration !== "v3" || follow.rcOnly === true) {
15335
+ this.pushEvent(follow, "assistant/chunk", { turn: follow.turn, step: 1, chunk });
15336
+ return;
15337
+ }
15338
+ let attempt = follow.assistantAttempt;
15339
+ if (attempt === void 0) {
15340
+ attempt = {
15341
+ attemptId: `codex-attempt:${follow.activeTurnId ?? follow.turn}:${follow.nextSeq}`,
15342
+ startedAfterSeq: follow.nextSeq - 1,
15343
+ nextIndex: 0,
15344
+ stream: []
15345
+ };
15346
+ follow.assistantAttempt = attempt;
15347
+ follow.assistantStreamRevision += 1;
15348
+ follow.queue.push({
15349
+ type: "assistant-stream",
15350
+ frame: {
15351
+ type: "start",
15352
+ attemptId: attempt.attemptId,
15353
+ revision: follow.assistantStreamRevision,
15354
+ startedAfterSeq: attempt.startedAfterSeq,
15355
+ turn: follow.turn,
15356
+ step: 1
15357
+ }
15358
+ });
15359
+ }
15360
+ const time = Date.now();
15361
+ const index = attempt.nextIndex++;
15362
+ attempt.stream.push({ time, chunk });
15363
+ follow.assistantStreamRevision += 1;
15364
+ follow.queue.push({
15365
+ type: "assistant-stream",
15366
+ frame: {
15367
+ type: "chunk",
15368
+ attemptId: attempt.attemptId,
15369
+ revision: follow.assistantStreamRevision,
15370
+ index,
15371
+ time,
15372
+ chunk
15373
+ }
15374
+ });
15375
+ }
15376
+ endAssistantAttempt(follow, outcome) {
15377
+ const attempt = follow.assistantAttempt;
15378
+ if (attempt === void 0) return;
15379
+ follow.assistantAttempt = void 0;
15380
+ follow.assistantStreamRevision += 1;
15381
+ if (this.sessionGeneration === "v3" && follow.rcOnly !== true) {
15382
+ follow.queue.push({
15383
+ type: "assistant-stream",
15384
+ frame: {
15385
+ type: "end",
15386
+ attemptId: attempt.attemptId,
15387
+ revision: follow.assistantStreamRevision,
15388
+ index: attempt.nextIndex,
15389
+ outcome
15390
+ }
15391
+ });
15392
+ }
15393
+ }
15160
15394
  ensureToolStarted(follow, item) {
15161
15395
  const itemId = string(item.id);
15162
15396
  if (itemId === void 0) return;
@@ -15199,6 +15433,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15199
15433
  follow.streamedBlocks.clear();
15200
15434
  follow.nextBlockIndex = 0;
15201
15435
  follow.streamActive = false;
15436
+ follow.assistantAttempt = void 0;
15202
15437
  follow.liveItems.clear();
15203
15438
  follow.liveToolOutput.clear();
15204
15439
  follow.liveToolResultSeq.clear();
@@ -15211,7 +15446,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15211
15446
  this.broadcastRcHost({ type: "host/session-status", sessionId, running: args[1] });
15212
15447
  } else if (event === "api-session/removed" && sessionId !== void 0) {
15213
15448
  this.broadcastRcHost({ type: "host/session-removed", sessionId });
15214
- } else if (event === "api-session/added" && isRecord3(args[0])) {
15449
+ } else if (event === "api-session/added" && isRecord4(args[0])) {
15215
15450
  const summary = args[0];
15216
15451
  this.broadcastRcHost({
15217
15452
  type: "host/session-added",
@@ -15239,7 +15474,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15239
15474
  }
15240
15475
  async answerRemoteEvent(args, signal) {
15241
15476
  const eventId = string(args.eventId);
15242
- const outcome = record(args.outcome);
15477
+ const outcome = record2(args.outcome);
15243
15478
  if (eventId === void 0) throw new Error("The CodeX approval result is missing its event id.");
15244
15479
  const pending = this.pendingApprovals.get(eventId);
15245
15480
  if (pending === void 0) return void 0;
@@ -15290,7 +15525,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15290
15525
  if (!containsPath(workspace.path, path)) {
15291
15526
  return failure("workspace-not-found", "The selected directory is outside the current CodeX virtual Workspace.");
15292
15527
  }
15293
- const listing = record(await this.client.request("dsh/directoryList", { path }, signal));
15528
+ const listing = record2(await this.client.request("dsh/directoryList", { path }, signal));
15294
15529
  if (!isCodexDirectoryListing(listing)) {
15295
15530
  return failure("internal", "CodeX Remote returned an invalid directory listing.");
15296
15531
  }
@@ -15324,13 +15559,13 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15324
15559
  const directory = await this.models(signal);
15325
15560
  const selection = directory.default;
15326
15561
  const requestedPreset = CODEX_DEFAULT_PERMISSION;
15327
- const result = record(await this.client.request("thread/start", {
15562
+ const result = record2(await this.client.request("thread/start", {
15328
15563
  cwd,
15329
15564
  model: selection.model,
15330
15565
  permissionPreset: requestedPreset
15331
15566
  }, signal));
15332
15567
  const permissionPreset2 = codexPermissionPresetFromResponse(result) ?? requestedPreset;
15333
- const thread = { ...record(result.thread), cwd, turns: array(record(result.thread).turns) };
15568
+ const thread = { ...record2(result.thread), cwd, turns: array(record2(result.thread).turns) };
15334
15569
  const projected = projectCodexThread(thread);
15335
15570
  if (projected === void 0) return failure("internal", "CodeX returned an invalid Thread.");
15336
15571
  this.pendingThreads.set(projected.nativeId, thread);
@@ -15350,16 +15585,14 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15350
15585
  }
15351
15586
  async forkSession(request, signal) {
15352
15587
  const sessionId = requiredString(request.sessionId, "sessionId");
15353
- let permissionPreset2 = this.selectedPermissions.get(sessionId);
15354
- const result = record(await this.client.request("thread/fork", {
15355
- threadId: nativeThreadId(sessionId),
15356
- ...permissionPreset2 === void 0 ? {} : { permissionPreset: permissionPreset2 }
15588
+ const result = record2(await this.client.request("thread/fork", {
15589
+ threadId: nativeThreadId(sessionId)
15357
15590
  }, signal));
15358
- permissionPreset2 = codexPermissionPresetFromResponse(result) ?? permissionPreset2 ?? CODEX_DEFAULT_PERMISSION;
15359
- const projected = projectCodexThread(record(result.thread));
15591
+ const permissionPreset2 = codexPermissionPresetFromResponse(result);
15592
+ const projected = projectCodexThread(record2(result.thread));
15360
15593
  if (projected === void 0) return failure("internal", "CodeX returned an invalid forked Thread.");
15361
15594
  this.selectedModels.set(projected.id, this.modelSelection(sessionId, await this.models(signal)));
15362
- this.selectedPermissions.set(projected.id, permissionPreset2);
15595
+ this.setPermissionSelection(projected.id, permissionPreset2);
15363
15596
  await this.refreshAndPublishWorkspaces();
15364
15597
  const seq = this.nextProjectionSeq();
15365
15598
  this.publishProjection(projected.id, "title", displayTitle(projected), seq);
@@ -15372,7 +15605,6 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15372
15605
  if (input2 === void 0) return failure("attachment-error", "CodeX virtual Sessions accept text and pasted PNG, JPEG, WebP, or GIF images only.");
15373
15606
  const threadId = nativeThreadId(sessionId);
15374
15607
  const selection = this.modelSelection(sessionId, await this.models(signal));
15375
- let permissionPreset2 = this.selectedPermissions.get(sessionId);
15376
15608
  const isFreshBlankThread = this.blankThreads.has(threadId) || this.pendingThreads.has(threadId);
15377
15609
  await this.ensureRcFollow(sessionId);
15378
15610
  if (!isFreshBlankThread) {
@@ -15381,8 +15613,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15381
15613
  model: selection.model
15382
15614
  }, signal);
15383
15615
  const serverPreset = codexPermissionPresetFromResponse(resumeResult);
15384
- if (permissionPreset2 === void 0 && serverPreset !== void 0) {
15385
- permissionPreset2 = serverPreset;
15616
+ if (serverPreset !== void 0) {
15386
15617
  this.setPermissionSelection(sessionId, serverPreset);
15387
15618
  }
15388
15619
  }
@@ -15396,8 +15627,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15396
15627
  await this.client.request(mode, {
15397
15628
  threadId,
15398
15629
  input: input2,
15399
- ...codexModelParams(selection),
15400
- ...permissionPreset2 === void 0 ? {} : { permissionPreset: permissionPreset2 }
15630
+ ...codexModelParams(selection)
15401
15631
  }, signal);
15402
15632
  }
15403
15633
  this.blankThreads.delete(threadId);
@@ -15418,8 +15648,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15418
15648
  }
15419
15649
  async hydrateImageAttachments(sessionId, signal) {
15420
15650
  const threadId = nativeThreadId(sessionId);
15421
- const result = record(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15422
- const thread = record(result.thread);
15651
+ const result = record2(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15652
+ const thread = record2(result.thread);
15423
15653
  if (string(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
15424
15654
  this.cacheHistoryImages(projectCodexNativeHistory(thread, sessionId));
15425
15655
  }
@@ -15441,7 +15671,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15441
15671
  return success({ title, seq });
15442
15672
  }
15443
15673
  async sessionPage(request, signal) {
15444
- const sessionId = sessionIdFromAddress(record(request.address));
15674
+ const sessionId = sessionIdFromAddress(record2(request.address));
15445
15675
  const page = await this.readHistoryPage(nativeThreadId(sessionId), {
15446
15676
  beforeSeq: optionalNonNegativeInteger(request.beforeSeq),
15447
15677
  throughSeq: optionalInteger(request.throughSeq),
@@ -15454,15 +15684,15 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15454
15684
  return void 0;
15455
15685
  }
15456
15686
  async fetchThread(threadId, signal) {
15457
- const result = record(await this.client.request("thread/read", { threadId, includeTurns: false }, signal));
15458
- const thread = record(result.thread);
15687
+ const result = record2(await this.client.request("thread/read", { threadId, includeTurns: false }, signal));
15688
+ const thread = record2(result.thread);
15459
15689
  if (string(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
15460
15690
  return thread;
15461
15691
  }
15462
15692
  async readHistoryPage(threadId, page, signal) {
15463
15693
  let value;
15464
15694
  try {
15465
- value = record(await this.client.request("dsh/sessionHistory", {
15695
+ value = record2(await this.client.request("dsh/sessionHistory", {
15466
15696
  threadId,
15467
15697
  ...page.beforeSeq === void 0 ? {} : { beforeSeq: page.beforeSeq },
15468
15698
  ...page.throughSeq === void 0 ? {} : { throughSeq: page.throughSeq },
@@ -15470,11 +15700,11 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15470
15700
  }, signal));
15471
15701
  } catch (error) {
15472
15702
  if (!isLegacySessionHistoryUnsupported(error)) throw error;
15473
- const result = record(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15474
- const thread = record(result.thread);
15703
+ const result = record2(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15704
+ const thread = record2(result.thread);
15475
15705
  if (string(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
15476
15706
  value = paginateCodexNativeHistory(
15477
- projectCodexNativeHistory(thread, `codex:${threadId}`),
15707
+ projectCodexNativeHistory(thread, `codex:${threadId}`, this.sessionGeneration),
15478
15708
  {
15479
15709
  beforeSeq: page.beforeSeq,
15480
15710
  throughSeq: page.throughSeq,
@@ -15482,9 +15712,14 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15482
15712
  }
15483
15713
  );
15484
15714
  }
15715
+ value = adaptCodexHistoryPage(value, this.sessionGeneration);
15485
15716
  if (!isCodexNativeHistoryPage(value, `codex:${threadId}`)) {
15486
15717
  throw new Error("CodeX Remote returned an invalid paginated History.");
15487
15718
  }
15719
+ if (Object.hasOwn(value, "permissionPreset")) {
15720
+ const preset = value.permissionPreset;
15721
+ this.setPermissionSelection(`codex:${threadId}`, preset === "workspace-write" || preset === "danger-full-access" ? preset : void 0);
15722
+ }
15488
15723
  this.cacheHistoryImages(value);
15489
15724
  return value;
15490
15725
  }
@@ -15545,6 +15780,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15545
15780
  streamedBlocks: /* @__PURE__ */ new Map(),
15546
15781
  nextBlockIndex: 0,
15547
15782
  streamActive: false,
15783
+ assistantStreamRevision: 0,
15548
15784
  liveItems: /* @__PURE__ */ new Map(),
15549
15785
  liveToolOutput: /* @__PURE__ */ new Map(),
15550
15786
  liveToolResultSeq: /* @__PURE__ */ new Map(),
@@ -15595,7 +15831,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15595
15831
  }
15596
15832
  createApiProxy() {
15597
15833
  const call = (endpoint) => async (request, signal) => {
15598
- const payload = endpoint === "session.prompt" && isRecord3(request.payload) ? { ...request.payload, requestId: String(request.rpcId) } : request.payload;
15834
+ const payload = endpoint === "session.prompt" && isRecord4(request.payload) ? { ...request.payload, requestId: String(request.rpcId) } : request.payload;
15599
15835
  const result = await this.dispatch(rcEndpoint(endpoint), { args: { request: payload } }, signal ?? new AbortController().signal);
15600
15836
  return {
15601
15837
  rpcId: request.rpcId,
@@ -15692,7 +15928,7 @@ async function loadCatalog(client, signal, pendingThreads) {
15692
15928
  const threads = [];
15693
15929
  let cursor2;
15694
15930
  for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
15695
- const result = record(await client.request("thread/list", {
15931
+ const result = record2(await client.request("thread/list", {
15696
15932
  limit: CODEX_PAGE_LIMIT,
15697
15933
  sortKey: "updated_at",
15698
15934
  sortDirection: "desc",
@@ -15700,7 +15936,7 @@ async function loadCatalog(client, signal, pendingThreads) {
15700
15936
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
15701
15937
  }, signal));
15702
15938
  for (const value of array(result.data)) {
15703
- const thread = record(value);
15939
+ const thread = record2(value);
15704
15940
  if (string(thread.id) !== void 0) threads.push(thread);
15705
15941
  }
15706
15942
  cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
@@ -15752,7 +15988,7 @@ async function loadCodexProjects(client, signal) {
15752
15988
  let cursor2;
15753
15989
  try {
15754
15990
  for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
15755
- const result = record(await client.request("project/list", {
15991
+ const result = record2(await client.request("project/list", {
15756
15992
  limit: CODEX_PAGE_LIMIT,
15757
15993
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
15758
15994
  }, signal));
@@ -15774,18 +16010,18 @@ async function loadModelDirectory(client, signal) {
15774
16010
  let defaultModelId;
15775
16011
  let cursor2;
15776
16012
  for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
15777
- const result = record(await client.request("model/list", {
16013
+ const result = record2(await client.request("model/list", {
15778
16014
  limit: CODEX_PAGE_LIMIT,
15779
16015
  includeHidden: false,
15780
16016
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
15781
16017
  }, signal));
15782
16018
  for (const value of array(result.data)) {
15783
- const source = record(value);
16019
+ const source = record2(value);
15784
16020
  if (source.hidden === true) continue;
15785
16021
  const id2 = string(source.model) ?? string(source.id);
15786
16022
  if (id2 === void 0 || models.has(id2)) continue;
15787
16023
  const efforts = array(source.supportedReasoningEfforts).map((value2) => {
15788
- const effort = record(value2);
16024
+ const effort = record2(value2);
15789
16025
  const effortId = string(effort.reasoningEffort);
15790
16026
  if (effortId === void 0) return void 0;
15791
16027
  const description2 = string(effort.description);
@@ -15827,7 +16063,7 @@ async function loadModelDirectory(client, signal) {
15827
16063
  models
15828
16064
  };
15829
16065
  }
15830
- function projectCodexNativeHistory(thread, sessionId) {
16066
+ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legacy") {
15831
16067
  const entries = [];
15832
16068
  let seq = 0;
15833
16069
  let turnNumber = 0;
@@ -15840,7 +16076,7 @@ function projectCodexNativeHistory(thread, sessionId) {
15840
16076
  type,
15841
16077
  seq: eventSeq,
15842
16078
  time,
15843
- data,
16079
+ data: adaptEventData(type, data, sessionGeneration),
15844
16080
  ...sourceEventSeqs === void 0 ? {} : { sourceEventSeqs },
15845
16081
  ...isSurfaceEvent(type) ? { surfaceOp: "append" } : {}
15846
16082
  },
@@ -15849,8 +16085,8 @@ function projectCodexNativeHistory(thread, sessionId) {
15849
16085
  return eventSeq;
15850
16086
  };
15851
16087
  for (const rawTurn of array(thread.turns)) {
15852
- const turn = record(rawTurn);
15853
- const turnItems = array(turn.items).map(record);
16088
+ const turn = record2(rawTurn);
16089
+ const turnItems = array(turn.items).map(record2);
15854
16090
  const toolImages = turnItems.flatMap(toolOutputImageBlocks);
15855
16091
  let imageOwner = -1;
15856
16092
  for (let index = 0; index < turnItems.length; index += 1) {
@@ -15893,10 +16129,11 @@ function projectCodexNativeHistory(thread, sessionId) {
15893
16129
  const projected = projectCodexThread(thread);
15894
16130
  return {
15895
16131
  header: {
15896
- version: 1,
16132
+ version: sessionGeneration === "v3" ? 3 : 1,
15897
16133
  id: sessionId,
15898
16134
  createdAt: projected?.createdAt ?? Date.now(),
15899
- ...projected?.cwd === void 0 ? {} : { cwd: projected.cwd }
16135
+ ...projected?.cwd === void 0 ? {} : { cwd: projected.cwd },
16136
+ ...sessionGeneration === "v3" ? { isSeeded: false } : {}
15900
16137
  },
15901
16138
  entries,
15902
16139
  lastSeq: seq - 1,
@@ -15929,6 +16166,40 @@ function paginateCodexNativeHistory(history, request) {
15929
16166
  ...history.activeTurnId === void 0 ? {} : { activeTurnId: history.activeTurnId }
15930
16167
  };
15931
16168
  }
16169
+ function adaptEventData(type, data, sessionGeneration) {
16170
+ if (sessionGeneration !== "v3" || type !== "assistant/message") return data;
16171
+ const value = record2(data);
16172
+ return Array.isArray(value.stream) ? value : { ...value, stream: [] };
16173
+ }
16174
+ function adaptCodexHistoryPage(value, sessionGeneration) {
16175
+ if (sessionGeneration !== "v3") return value;
16176
+ const page = record2(value);
16177
+ const sourceHeader = record2(page.header);
16178
+ const { seedLength: _seedLength, ...header } = sourceHeader;
16179
+ const records = Array.isArray(page.records) ? page.records.map((raw) => {
16180
+ const entry = record2(raw);
16181
+ const sourceEvent = record2(entry.event);
16182
+ const sourceSurfaceOp = record2(sourceEvent.surfaceOp);
16183
+ const surfaceOp = sourceEvent.surfaceOp === "append" ? "append" : sourceSurfaceOp.op === "replace" && integer(sourceSurfaceOp.startSeq) !== void 0 && integer(sourceSurfaceOp.endSeq) !== void 0 ? sourceSurfaceOp : sourceSurfaceOp.op === "replace" && integer(sourceSurfaceOp.start) !== void 0 && integer(sourceSurfaceOp.end) !== void 0 ? {
16184
+ op: "replace",
16185
+ startSeq: sourceSurfaceOp.start,
16186
+ endSeq: sourceSurfaceOp.end
16187
+ } : sourceEvent.surfaceOp;
16188
+ return {
16189
+ ...entry,
16190
+ event: {
16191
+ ...sourceEvent,
16192
+ data: adaptEventData(string(sourceEvent.type) ?? "", sourceEvent.data, sessionGeneration),
16193
+ ...surfaceOp === void 0 ? {} : { surfaceOp }
16194
+ }
16195
+ };
16196
+ }) : page.records;
16197
+ return {
16198
+ ...page,
16199
+ header: { ...header, version: 3, isSeeded: sourceHeader.isSeeded === true },
16200
+ records
16201
+ };
16202
+ }
15932
16203
  function itemEvents(item, turn, step, requestId, selection = modelSelection(), sessionId = CODEX_SESSION_PREFIX, supplementalImages = []) {
15933
16204
  const type = string(item.type);
15934
16205
  const id2 = string(item.id) ?? `${turn}:${step}:${hashString(JSON.stringify(item))}`;
@@ -15999,15 +16270,15 @@ function messageContent(item, attachmentSeed, fallbackType, fallbackText, supple
15999
16270
  }
16000
16271
  function toolOutputImageBlocks(item) {
16001
16272
  const candidates = [
16002
- record(item.result).content,
16003
- record(item.output).content,
16273
+ record2(item.result).content,
16274
+ record2(item.output).content,
16004
16275
  item.contentItems,
16005
16276
  Array.isArray(item.output) ? item.output : void 0
16006
16277
  ];
16007
16278
  for (const candidate of candidates) {
16008
16279
  if (!Array.isArray(candidate)) continue;
16009
16280
  const images = candidate.flatMap((value) => {
16010
- const block = record(value);
16281
+ const block = record2(value);
16011
16282
  return isImageContent2(block) && parseImageBlock(block) !== void 0 ? [block] : [];
16012
16283
  });
16013
16284
  if (images.length > 0) return images;
@@ -16024,7 +16295,7 @@ function contentBlocks(value, attachmentSeed) {
16024
16295
  blocks.push({ type: "text", text: raw });
16025
16296
  continue;
16026
16297
  }
16027
- const block = record(raw);
16298
+ const block = record2(raw);
16028
16299
  const image = imageContentBlock(block, attachmentSeed, imageIndex);
16029
16300
  if (image !== void 0) {
16030
16301
  imageIndex += 1;
@@ -16063,7 +16334,7 @@ function isImageContent2(block) {
16063
16334
  return block.type === "image" || block.type === "input_image";
16064
16335
  }
16065
16336
  function parseImageBlock(block) {
16066
- const url = string(block.url) ?? string(block.image_url) ?? string(record(block.image_url).url);
16337
+ const url = string(block.url) ?? string(block.image_url) ?? string(record2(block.image_url).url);
16067
16338
  const parsed = url === void 0 ? void 0 : parseDataImageUrl2(url);
16068
16339
  if (parsed !== void 0) return parsed;
16069
16340
  const data = string(block.data);
@@ -16085,7 +16356,7 @@ function collectImageBlocks(value, output = []) {
16085
16356
  for (const item of value) collectImageBlocks(item, output);
16086
16357
  return output;
16087
16358
  }
16088
- if (!isRecord3(value)) return output;
16359
+ if (!isRecord4(value)) return output;
16089
16360
  if (value.type === "image") output.push(value);
16090
16361
  for (const [key, child] of Object.entries(value)) {
16091
16362
  if (key === "attachment") continue;
@@ -16220,10 +16491,10 @@ function toolResultEvent(item, turn, step, id2, resultText) {
16220
16491
  };
16221
16492
  }
16222
16493
  function toolResultContentReplacement(event) {
16223
- const data = record(event.data);
16224
- const message = record(data.message);
16494
+ const data = record2(event.data);
16495
+ const message = record2(data.message);
16225
16496
  const content = array(message.content);
16226
- const result = record(content[0]);
16497
+ const result = record2(content[0]);
16227
16498
  if (result.isError === void 0) return event;
16228
16499
  const { isError: _isError, ...stableResult } = result;
16229
16500
  return {
@@ -16263,9 +16534,9 @@ function toolResultText(item, type, text) {
16263
16534
  if (type === "exitedReviewMode") return `Exited review mode${string(item.review) ? `: ${string(item.review)}` : "."}`;
16264
16535
  if (type === "sleep") return `Waited ${integer(item.durationMs) ?? 0} ms.`;
16265
16536
  if (type === "subAgentActivity") {
16266
- return [string(record(item.kind).type) ?? string(item.kind) ?? "Subagent activity", string(item.agentPath)].filter((value) => value !== void 0).join(": ");
16537
+ return [string(record2(item.kind).type) ?? string(item.kind) ?? "Subagent activity", string(item.agentPath)].filter((value) => value !== void 0).join(": ");
16267
16538
  }
16268
- return text ?? itemText2(record(item.result)) ?? itemText2(record(item.output)) ?? string(item.status) ?? type;
16539
+ return text ?? itemText2(record2(item.result)) ?? itemText2(record2(item.output)) ?? string(item.status) ?? type;
16269
16540
  }
16270
16541
  function isSurfaceEvent(type) {
16271
16542
  return type === "user/message" || type === "assistant/message" || type === "tool/result";
@@ -16337,10 +16608,10 @@ function toolResultView(item, type, resultText) {
16337
16608
  };
16338
16609
  }
16339
16610
  function toolDisplayName(item, type) {
16340
- return string(item.name) ?? string(item.tool) ?? string(record(item.tool).name) ?? (type === "mcpToolCall" ? "CodeX MCP tool" : humanizeItemType(type));
16611
+ return string(item.name) ?? string(item.tool) ?? string(record2(item.tool).name) ?? (type === "mcpToolCall" ? "CodeX MCP tool" : humanizeItemType(type));
16341
16612
  }
16342
16613
  function fileLocations(item) {
16343
- return array(item.changes).map((value) => string(record(value).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16614
+ return array(item.changes).map((value) => string(record2(value).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16344
16615
  }
16345
16616
  function nativeWorkspace(view) {
16346
16617
  const { sessionCount: _sessionCount, ...workspace } = view;
@@ -16362,10 +16633,10 @@ function projectWorkspace(project) {
16362
16633
  };
16363
16634
  }
16364
16635
  function projectCodexProject(value, fallbackPosition) {
16365
- const source = record(value);
16636
+ const source = record2(value);
16366
16637
  const id2 = string(source.id);
16367
16638
  if (id2 === void 0) return void 0;
16368
- const roots = array(source.roots).map((root) => string(record(root).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16639
+ const roots = array(source.roots).map((root) => string(record2(root).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16369
16640
  if (roots.length === 0) return void 0;
16370
16641
  return {
16371
16642
  id: id2,
@@ -16415,7 +16686,7 @@ function codexPromptInput(content) {
16415
16686
  if (content.length === 0 || content.length > MAX_CODEX_PROMPT_PARTS) return void 0;
16416
16687
  const input2 = [];
16417
16688
  for (const value of content) {
16418
- if (!isRecord3(value)) return void 0;
16689
+ if (!isRecord4(value)) return void 0;
16419
16690
  if (value.type === "text") {
16420
16691
  if (typeof value.text !== "string" || value.text.length === 0 || value.text.length > MAX_CODEX_PROMPT_TEXT) return void 0;
16421
16692
  input2.push({ type: "text", text: value.text });
@@ -16436,7 +16707,7 @@ function isCanonicalBase642(value) {
16436
16707
  function modelSelectionProjection(selection) {
16437
16708
  return { lastUsed: selection, next: selection };
16438
16709
  }
16439
- function codexPermissionsProjection(currentValue) {
16710
+ function codexPermissionsProjection(preset) {
16440
16711
  return {
16441
16712
  options: [
16442
16713
  {
@@ -16450,7 +16721,7 @@ function codexPermissionsProjection(currentValue) {
16450
16721
  description: "Full Host file access without approval prompts for subsequent turns in this virtual Session."
16451
16722
  }
16452
16723
  ],
16453
- currentValue
16724
+ currentValue: preset ?? "Host settings (not reported)"
16454
16725
  };
16455
16726
  }
16456
16727
  function codexImageLimitsProjection() {
@@ -16466,22 +16737,6 @@ function codexImageLimitsProjection() {
16466
16737
  function isCodexPermissionPreset(value) {
16467
16738
  return value === "workspace-write" || value === "danger-full-access";
16468
16739
  }
16469
- function codexPermissionPresetFromResponse(value) {
16470
- const root = record(value);
16471
- return codexPermissionPresetFromPolicyRecord(root) ?? codexPermissionPresetFromPolicyRecord(record(root.threadSettings));
16472
- }
16473
- function codexPermissionPresetFromPolicyRecord(value) {
16474
- const sandbox = value.sandbox ?? value.sandboxPolicy;
16475
- if (value.approvalPolicy === "never" && isDangerFullAccessSandbox(sandbox)) return "danger-full-access";
16476
- if (value.approvalPolicy === "on-request" && isWorkspaceWriteSandbox(sandbox)) return "workspace-write";
16477
- return void 0;
16478
- }
16479
- function isDangerFullAccessSandbox(value) {
16480
- return value === "danger-full-access" || record(value).type === "dangerFullAccess";
16481
- }
16482
- function isWorkspaceWriteSandbox(value) {
16483
- return value === "workspace-write" || record(value).type === "workspaceWrite";
16484
- }
16485
16740
  function modelCatalog(directory) {
16486
16741
  return {
16487
16742
  default: directory.default,
@@ -16520,7 +16775,7 @@ function itemText2(value) {
16520
16775
  if (content !== void 0) {
16521
16776
  const text = content.map((part) => {
16522
16777
  if (typeof part === "string") return part;
16523
- const block = record(part);
16778
+ const block = record2(part);
16524
16779
  return string(block.text) ?? string(block.content) ?? "";
16525
16780
  }).filter(Boolean).join("\n");
16526
16781
  if (text !== "") return text;
@@ -16534,8 +16789,8 @@ function toolArguments(item) {
16534
16789
  if (item.type === "commandExecution") return { command: commandText(item.command) ?? item.command ?? "" };
16535
16790
  if (item.type === "fileChange") return {
16536
16791
  changes: array(item.changes).map((value) => {
16537
- const change = record(value);
16538
- const kind = typeof change.kind === "string" ? change.kind : string(record(change.kind).type);
16792
+ const change = record2(value);
16793
+ const kind = typeof change.kind === "string" ? change.kind : string(record2(change.kind).type);
16539
16794
  return {
16540
16795
  ...typeof change.path === "string" ? { path: change.path } : {},
16541
16796
  ...kind === void 0 ? {} : { kind }
@@ -16556,7 +16811,7 @@ function toolArguments(item) {
16556
16811
  if (item.type === "subAgentActivity") return {
16557
16812
  agentThreadId: string(item.agentThreadId) ?? "",
16558
16813
  agentPath: string(item.agentPath) ?? "",
16559
- kind: string(record(item.kind).type) ?? string(item.kind) ?? "activity"
16814
+ kind: string(record2(item.kind).type) ?? string(item.kind) ?? "activity"
16560
16815
  };
16561
16816
  if (item.type === "sleep") return { durationMs: integer(item.durationMs) ?? 0 };
16562
16817
  if (item.type === "enteredReviewMode" || item.type === "exitedReviewMode") return {
@@ -16567,19 +16822,19 @@ function toolArguments(item) {
16567
16822
  }
16568
16823
  function fileChangeSummary(item) {
16569
16824
  const changes = array(item.changes).map((value) => {
16570
- const change = record(value);
16825
+ const change = record2(value);
16571
16826
  const path = string(change.path);
16572
- const kind = typeof change.kind === "string" ? change.kind : string(record(change.kind).type);
16827
+ const kind = typeof change.kind === "string" ? change.kind : string(record2(change.kind).type);
16573
16828
  return [kind, path].filter((part) => part !== void 0 && part.length > 0).join(" ");
16574
16829
  }).filter(Boolean);
16575
16830
  return changes.length === 0 ? "File changes completed." : changes.join("\n");
16576
16831
  }
16577
16832
  function sanitizedFileChanges(value) {
16578
16833
  return array(value).flatMap((raw) => {
16579
- const change = record(raw);
16834
+ const change = record2(raw);
16580
16835
  const path = string(change.path);
16581
16836
  if (path === void 0) return [];
16582
- const kind = typeof change.kind === "string" ? change.kind : string(record(change.kind).type);
16837
+ const kind = typeof change.kind === "string" ? change.kind : string(record2(change.kind).type);
16583
16838
  return [{ path, ...kind === void 0 ? {} : { kind } }];
16584
16839
  });
16585
16840
  }
@@ -16609,7 +16864,7 @@ function humanizeItemType(type) {
16609
16864
  function commandText(value) {
16610
16865
  if (typeof value === "string") return value;
16611
16866
  if (Array.isArray(value)) return value.filter((item) => typeof item === "string").join(" ");
16612
- const source = record(value);
16867
+ const source = record2(value);
16613
16868
  return string(source.command) ?? string(source.text);
16614
16869
  }
16615
16870
  function sessionIdFromAddress(address) {
@@ -16625,7 +16880,7 @@ function nativeThreadId(sessionId) {
16625
16880
  function activeTurnId(value) {
16626
16881
  if (!Array.isArray(value)) return void 0;
16627
16882
  for (let index = value.length - 1; index >= 0; index -= 1) {
16628
- const turn = record(value[index]);
16883
+ const turn = record2(value[index]);
16629
16884
  if (isActiveCodexTurn(turn) && typeof turn.id === "string" && turn.id.length > 0) return turn.id;
16630
16885
  }
16631
16886
  return void 0;
@@ -16634,10 +16889,10 @@ function isActiveCodexTurn(turn) {
16634
16889
  return turn.status === "inProgress" || turn.status === "running";
16635
16890
  }
16636
16891
  function carrierArgs(payload) {
16637
- return record(record(payload).args);
16892
+ return record2(record2(payload).args);
16638
16893
  }
16639
16894
  function requestArg(args) {
16640
- return record(args.request ?? args._request ?? args);
16895
+ return record2(args.request ?? args._request ?? args);
16641
16896
  }
16642
16897
  function rcEndpoint(endpoint) {
16643
16898
  if (endpoint === "workspace.list") return "workspace/list";
@@ -16653,20 +16908,20 @@ function ok(value) {
16653
16908
  return value === void 0 ? { ok: true } : { ok: true, value };
16654
16909
  }
16655
16910
  function business(value) {
16656
- const result = record(value);
16911
+ const result = record2(value);
16657
16912
  if (result.ok === true) return ok(result.value);
16658
16913
  if (result.ok === false) {
16659
- const error = record(result.error);
16660
- return fail(string(error.code) ?? "internal", string(error.message) ?? "CodeX virtual Harness rejected the request.", record(error.details));
16914
+ const error = record2(result.error);
16915
+ return fail(string(error.code) ?? "internal", string(error.message) ?? "CodeX virtual Harness rejected the request.", record2(error.details));
16661
16916
  }
16662
16917
  return ok(value);
16663
16918
  }
16664
16919
  function isCodexDirectoryListing(value) {
16665
- const listing = record(value);
16920
+ const listing = record2(value);
16666
16921
  return typeof listing.path === "string" && typeof listing.home === "string" && Array.isArray(listing.crumbs) && listing.crumbs.every(isCodexDirectoryEntry) && Array.isArray(listing.entries) && listing.entries.every(isCodexDirectoryEntry) && typeof listing.truncated === "boolean";
16667
16922
  }
16668
16923
  function isCodexDirectoryEntry(value) {
16669
- const entry = record(value);
16924
+ const entry = record2(value);
16670
16925
  return typeof entry.name === "string" && typeof entry.path === "string" && typeof entry.hidden === "boolean";
16671
16926
  }
16672
16927
  function fail(code, message, details = {}) {
@@ -16684,12 +16939,12 @@ function errorCode(error) {
16684
16939
  return "code" in error && typeof error.code === "string" ? error.code : void 0;
16685
16940
  }
16686
16941
  function errorDetails(error) {
16687
- return "details" in error && isRecord3(error.details) ? error.details : {};
16942
+ return "details" in error && isRecord4(error.details) ? error.details : {};
16688
16943
  }
16689
- function record(value) {
16690
- return isRecord3(value) ? value : {};
16944
+ function record2(value) {
16945
+ return isRecord4(value) ? value : {};
16691
16946
  }
16692
- function isRecord3(value) {
16947
+ function isRecord4(value) {
16693
16948
  return typeof value === "object" && value !== null && !Array.isArray(value);
16694
16949
  }
16695
16950
  function array(value) {
@@ -16721,8 +16976,8 @@ function optionalPositiveInteger(value) {
16721
16976
  return parsed;
16722
16977
  }
16723
16978
  function isCodexNativeHistoryPage(value, sessionId) {
16724
- const page = record(value);
16725
- const header = record(page.header);
16979
+ const page = record2(value);
16980
+ const header = record2(page.header);
16726
16981
  return header.id === sessionId && integer(page.cursor) !== void 0 && integer(page.nextTurn) !== void 0 && Array.isArray(page.records) && typeof page.hasMore === "boolean" && (page.activeTurnId === void 0 || typeof page.activeTurnId === "string");
16727
16982
  }
16728
16983
  function truncateCodePoints(value, maximum) {
@@ -16903,7 +17158,7 @@ function normalizeServerUrl(value) {
16903
17158
  }
16904
17159
 
16905
17160
  // src/version.ts
16906
- var PLUGIN_VERSION = "0.4.9";
17161
+ var PLUGIN_VERSION = "0.4.12";
16907
17162
 
16908
17163
  // src/server-api.ts
16909
17164
  var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
@@ -17218,10 +17473,11 @@ var ServerApiError = class extends Error {
17218
17473
  }
17219
17474
  };
17220
17475
  function validateTokens(value) {
17221
- if (typeof value.accessToken !== "string" || value.accessToken.length < 16 || typeof value.refreshToken !== "string" || value.refreshToken.length < 16 || !Number.isSafeInteger(value.accessTokenExpiresAt) || !Number.isSafeInteger(value.refreshTokenExpiresAt)) {
17476
+ const parsed = deviceTokenPairSchema.safeParse(value);
17477
+ if (!parsed.success) {
17222
17478
  throw new ServerApiError("INVALID_MESSAGE", "The Server returned invalid device credentials.", false);
17223
17479
  }
17224
- return value;
17480
+ return parsed.data;
17225
17481
  }
17226
17482
  function validateWebLogin(value) {
17227
17483
  const item = requireRecord(value, "account login");
@@ -17421,7 +17677,7 @@ var TypertGatewaySwitch = class {
17421
17677
  if (normalized !== void 0) return normalized;
17422
17678
  const source = error instanceof Error ? error : new Error("The Harness Gateway rejected the request.");
17423
17679
  const code = "code" in source && typeof source.code === "string" ? source.code : "internal";
17424
- const details = "details" in source && isRecord4(source.details) ? source.details : {};
17680
+ const details = "details" in source && isRecord5(source.details) ? source.details : {};
17425
17681
  return { code, message: source.message, details };
17426
17682
  }
17427
17683
  };
@@ -17430,7 +17686,7 @@ function requestFromCarrier(endpoint, payload, signal) {
17430
17686
  if (segments.length !== 2 || segments.some((segment) => segment.length === 0)) {
17431
17687
  throw new Error("The Harness Gateway endpoint is invalid.");
17432
17688
  }
17433
- if (!isRecord4(payload) || !isRecord4(payload.args)) {
17689
+ if (!isRecord5(payload) || !isRecord5(payload.args)) {
17434
17690
  throw new Error("The Harness Gateway payload is invalid.");
17435
17691
  }
17436
17692
  return { namespace: segments[0], method: segments[1], args: payload.args, signal };
@@ -17445,7 +17701,7 @@ function isLocalOnlyEndpoint(endpoint) {
17445
17701
  function isRemoteCommandMethod(method) {
17446
17702
  return REMOTE_COMMAND_METHODS.includes(method);
17447
17703
  }
17448
- function isRecord4(value) {
17704
+ function isRecord5(value) {
17449
17705
  return typeof value === "object" && value !== null && !Array.isArray(value);
17450
17706
  }
17451
17707
 
@@ -17456,7 +17712,7 @@ import { networkInterfaces } from "node:os";
17456
17712
  // src/native-rtc-helper.ts
17457
17713
  import { spawn, spawnSync } from "node:child_process";
17458
17714
  import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
17459
- import { delimiter, dirname, join } from "node:path";
17715
+ import { delimiter, dirname as dirname2, join as join2 } from "node:path";
17460
17716
  import { fileURLToPath } from "node:url";
17461
17717
  var cachedExternalFactory;
17462
17718
  var cachedExternalFactoryResolved = false;
@@ -17507,21 +17763,21 @@ function nodeBinaryCandidates() {
17507
17763
  add3(process.env.DSH_REMOTE_NODE);
17508
17764
  add3(process.env.NODE);
17509
17765
  add3(process.execPath);
17510
- for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join(part, process.platform === "win32" ? "node.exe" : "node"));
17766
+ for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join2(part, process.platform === "win32" ? "node.exe" : "node"));
17511
17767
  add3("/opt/homebrew/bin/node");
17512
17768
  add3("/usr/local/bin/node");
17513
17769
  add3("/usr/bin/node");
17514
- add3(join(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
17515
- add3(join(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
17516
- add3(join(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
17770
+ add3(join2(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
17771
+ add3(join2(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
17772
+ add3(join2(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
17517
17773
  for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
17518
17774
  return candidates;
17519
17775
  }
17520
17776
  function nvmNodeCandidates() {
17521
- const root = join(process.env.HOME ?? "", ".nvm", "versions", "node");
17777
+ const root = join2(process.env.HOME ?? "", ".nvm", "versions", "node");
17522
17778
  if (root === "" || !existsSync(root)) return [];
17523
17779
  try {
17524
- return readdirSync(root).map((version) => join(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
17780
+ return readdirSync(root).map((version) => join2(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
17525
17781
  } catch {
17526
17782
  return [];
17527
17783
  }
@@ -17543,7 +17799,7 @@ function isUsableExternalNode(candidate, requireFrom) {
17543
17799
  "console.log(`node:${process.versions.node}`);"
17544
17800
  ].join("")
17545
17801
  ], {
17546
- cwd: dirname(requireFrom),
17802
+ cwd: dirname2(requireFrom),
17547
17803
  env,
17548
17804
  encoding: "utf8",
17549
17805
  timeout: 3e3
@@ -17565,7 +17821,7 @@ var ExternalNativePeerConnection = class {
17565
17821
  this.nodeBinary = nodeBinary;
17566
17822
  this.requireFrom = requireFrom;
17567
17823
  this.child = spawn(this.nodeBinary, ["--input-type=module", "--eval", HELPER_SOURCE], {
17568
- cwd: dirname(this.requireFrom),
17824
+ cwd: dirname2(this.requireFrom),
17569
17825
  env: { ...process.env, DSH_REMOTE_RTC_HELPER_REQUIRE_FROM: this.requireFrom },
17570
17826
  stdio: ["pipe", "pipe", "pipe"]
17571
17827
  });
@@ -18516,6 +18772,7 @@ function isPrivate(ip) {
18516
18772
  var REMOTE_COMMAND_LIST_MIN_VERSION = [0, 3, 16];
18517
18773
  var REMOTE_FILE_VIEWER_MIN_VERSION = [0, 3, 17];
18518
18774
  var DIRECT_WEBRTC_NEGOTIATE_TIMEOUT_MS = 12e3;
18775
+ var DIRECT_LAN_PROGRESS_DISPLAY_MS = 1400;
18519
18776
  var ClientModeRuntime = class {
18520
18777
  constructor(config, identities, server, apiProxy, typertGateway, logger, host, rtcFactoryProvider = loadNodeRtcFactory) {
18521
18778
  this.config = config;
@@ -18774,7 +19031,7 @@ var ClientModeRuntime = class {
18774
19031
  const virtual = CodexVirtualHarness.remote(remote.client, {
18775
19032
  deviceId: remote.target.deviceId,
18776
19033
  name: remote.target.name
18777
- });
19034
+ }, harnessSessionGeneration(this.host?.localHarnessVersion?.()));
18778
19035
  let workspace;
18779
19036
  try {
18780
19037
  workspace = await virtual.selectWorkspace(workspaceId, signal);
@@ -18804,12 +19061,12 @@ var ClientModeRuntime = class {
18804
19061
  throw new ClientModeError("FEATURE_NOT_SUPPORTED", "The selected Host does not provide CodeX workspaces.");
18805
19062
  }
18806
19063
  this.assertRemoteCompatible(remote);
18807
- const result = record2(await new CodexRemoteClient(remote.client).request("project/create", {
19064
+ const result = record3(await new CodexRemoteClient(remote.client).request("project/create", {
18808
19065
  name: remoteWorkspaceTitle(trimmedPath),
18809
19066
  roots: [{ path: trimmedPath }],
18810
19067
  idempotencyKey: uuidV7()
18811
19068
  }, signal));
18812
- const project = record2(result.project);
19069
+ const project = record3(result.project);
18813
19070
  if (typeof project.id !== "string" || project.id.length === 0) {
18814
19071
  throw new ClientModeError("INVALID_MESSAGE", "The Host returned an invalid CodeX project.");
18815
19072
  }
@@ -18859,7 +19116,7 @@ var ClientModeRuntime = class {
18859
19116
  }
18860
19117
  async openCodexStream(payload, signal) {
18861
19118
  const remote = this.activeCodexRemote();
18862
- const value = record2(payload);
19119
+ const value = record3(payload);
18863
19120
  if (typeof value.streamId !== "string" || value.streamId.length === 0 || value.streamId.length > 128 || typeof value.threadId !== "string" || value.threadId.length === 0) {
18864
19121
  throw new ClientModeError("INVALID_MESSAGE", "A Codex stream and thread are required.");
18865
19122
  }
@@ -18891,10 +19148,10 @@ var ClientModeRuntime = class {
18891
19148
  }
18892
19149
  }
18893
19150
  stream.unsubscribe = remote.client.onEvent((event) => {
18894
- if (event.event === "codex.app.frame" && isRecord5(event.data) && event.data.streamId === value.streamId) {
19151
+ if (event.event === "codex.app.frame" && isRecord6(event.data) && event.data.streamId === value.streamId) {
18895
19152
  this.appendCodexFrame(stream, event.data);
18896
19153
  }
18897
- if (event.event === "codex.app.stream.closed" && isRecord5(event.data) && event.data.streamId === value.streamId) {
19154
+ if (event.event === "codex.app.stream.closed" && isRecord6(event.data) && event.data.streamId === value.streamId) {
18898
19155
  stream.closed = typeof event.data.reason === "string" ? event.data.reason : "closed";
18899
19156
  stream.wake();
18900
19157
  }
@@ -18921,7 +19178,7 @@ var ClientModeRuntime = class {
18921
19178
  stream.wake();
18922
19179
  };
18923
19180
  appendCodexFrame(stream, data) {
18924
- if (!isRecord5(data) || !isRecord5(data.frame) || typeof data.frame.method !== "string") return;
19181
+ if (!isRecord6(data) || !isRecord6(data.frame) || typeof data.frame.method !== "string") return;
18925
19182
  if (stream.frames.length >= 256) {
18926
19183
  stream.closed = "overflow";
18927
19184
  } else {
@@ -18944,7 +19201,7 @@ var ClientModeRuntime = class {
18944
19201
  };
18945
19202
  }
18946
19203
  async nextCodexFrames(payload, signal) {
18947
- const value = record2(payload);
19204
+ const value = record3(payload);
18948
19205
  if (typeof value.streamId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Codex stream is required.");
18949
19206
  const stream = this.codexStreams.get(value.streamId);
18950
19207
  if (stream === void 0) throw new ClientModeError("STREAM_NOT_FOUND", "The Codex stream is not open.");
@@ -18958,7 +19215,7 @@ var ClientModeRuntime = class {
18958
19215
  };
18959
19216
  }
18960
19217
  async closeCodexStream(payload) {
18961
- const value = record2(payload);
19218
+ const value = record3(payload);
18962
19219
  if (typeof value.streamId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Codex stream is required.");
18963
19220
  const stream = this.codexStreams.get(value.streamId);
18964
19221
  if (stream === void 0) return { closed: false, streamId: value.streamId };
@@ -19006,7 +19263,16 @@ var ClientModeRuntime = class {
19006
19263
  }
19007
19264
  assertRemoteCompatible(remote) {
19008
19265
  const localRemoteGateway = this.gatewaySwitch.supportsCarrier();
19009
- if (localRemoteGateway && remote.features.remoteGateway || !localRemoteGateway && this.proxySwitch !== void 0 && remote.features.apiProxy) return;
19266
+ if (localRemoteGateway && remote.features.remoteGateway) {
19267
+ const localSessionGeneration = harnessSessionGeneration(this.host?.localHarnessVersion?.());
19268
+ const remoteSessionGeneration = remote.features.sessionFormat === 3 ? "v3" : "legacy";
19269
+ if (localSessionGeneration === remoteSessionGeneration) return;
19270
+ throw new ClientModeError(
19271
+ "HARNESS_VERSION_INCOMPATIBLE",
19272
+ `The local and remote Harness Session formats differ (${localSessionGeneration} vs ${remoteSessionGeneration}).`
19273
+ );
19274
+ }
19275
+ if (!localRemoteGateway && this.proxySwitch !== void 0 && remote.features.apiProxy) return;
19010
19276
  throw new ClientModeError(
19011
19277
  "HARNESS_VERSION_INCOMPATIBLE",
19012
19278
  localRemoteGateway ? "The selected Host does not provide the Harness v0.1.2 Typert Remote Gateway transport." : "The selected Host does not provide the legacy Harness ApiProxy transport."
@@ -19069,11 +19335,15 @@ var ClientModeRuntime = class {
19069
19335
  let transport;
19070
19336
  for (const attempt of attempts) {
19071
19337
  webRtcFallback = false;
19072
- this.updateConnectionProgress(progressRunId, "probing", activeTransportsForAttempt(attempt));
19073
- transport = createTransport(attempt);
19074
- client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target), 6e4);
19075
- await client.connect();
19076
- signal?.throwIfAborted();
19338
+ const stopProgressTimer = this.beginAttemptProgress(progressRunId, attempt);
19339
+ try {
19340
+ transport = createTransport(attempt);
19341
+ client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target), 6e4);
19342
+ await client.connect();
19343
+ signal?.throwIfAborted();
19344
+ } finally {
19345
+ stopProgressTimer();
19346
+ }
19077
19347
  if (attempt === "relay" || !webRtcFallback) break;
19078
19348
  await client.close();
19079
19349
  client = void 0;
@@ -19131,7 +19401,8 @@ var ClientModeRuntime = class {
19131
19401
  transport: connectedTransport,
19132
19402
  features,
19133
19403
  progressRunId,
19134
- ...serverDevice.clientVersion === void 0 ? {} : { clientVersion: serverDevice.clientVersion }
19404
+ ...serverDevice.clientVersion === void 0 ? {} : { clientVersion: serverDevice.clientVersion },
19405
+ ...serverDevice.harnessVersion === void 0 ? {} : { harnessVersion: serverDevice.harnessVersion }
19135
19406
  };
19136
19407
  } catch (error) {
19137
19408
  this.clearConnectionProgress(progressRunId);
@@ -19148,6 +19419,19 @@ var ClientModeRuntime = class {
19148
19419
  await previous?.client.close().catch(() => void 0);
19149
19420
  return next;
19150
19421
  }
19422
+ beginAttemptProgress(runId, attempt) {
19423
+ if (attempt !== "direct") {
19424
+ this.updateConnectionProgress(runId, "probing", [attempt]);
19425
+ return () => void 0;
19426
+ }
19427
+ this.updateConnectionProgress(runId, "probing", ["lan"]);
19428
+ const timer = setTimeout(() => {
19429
+ if (this.connectionProgress?.runId === runId && this.connectionProgress.phase === "probing") {
19430
+ this.updateConnectionProgress(runId, "probing", ["p2p"]);
19431
+ }
19432
+ }, DIRECT_LAN_PROGRESS_DISPLAY_MS);
19433
+ return () => clearTimeout(timer);
19434
+ }
19151
19435
  updateConnectionProgress(runId, phase, activeTransports) {
19152
19436
  if (this.connectionProgress?.runId !== runId) return;
19153
19437
  this.connectionProgress = {
@@ -19165,14 +19449,14 @@ var ClientModeRuntime = class {
19165
19449
  if (endpoint === "status") return ok2(await this.detailedStatus());
19166
19450
  if (endpoint === "devices") return ok2(await this.devices());
19167
19451
  if (endpoint === "client.account.login") {
19168
- const value = record2(payload);
19452
+ const value = record3(payload);
19169
19453
  if (typeof value.email !== "string" || typeof value.password !== "string") {
19170
19454
  throw new ClientModeError("INVALID_MESSAGE", "Email and password are required.");
19171
19455
  }
19172
19456
  return ok2(await this.authorizeClientWithAccount(value.email, value.password));
19173
19457
  }
19174
19458
  if (endpoint === "client.account.qr.start") {
19175
- const value = record2(payload);
19459
+ const value = record3(payload);
19176
19460
  const provider = value.provider ?? "zhihu";
19177
19461
  if (provider !== "zhihu" && provider !== "github") {
19178
19462
  throw new ClientModeError("INVALID_MESSAGE", "A supported OAuth provider is required.");
@@ -19180,14 +19464,14 @@ var ClientModeRuntime = class {
19180
19464
  return ok2(await this.startClientOAuthQrLogin(provider));
19181
19465
  }
19182
19466
  if (endpoint === "client.account.qr.poll") {
19183
- const value = record2(payload);
19467
+ const value = record3(payload);
19184
19468
  if (typeof value.qrId !== "string" || value.qrId.length < 20) {
19185
19469
  throw new ClientModeError("INVALID_MESSAGE", "A QR login session is required.");
19186
19470
  }
19187
19471
  return ok2(await this.pollClientOAuthQrLogin(value.qrId));
19188
19472
  }
19189
19473
  if (endpoint === "directory.list") {
19190
- const value = record2(payload);
19474
+ const value = record3(payload);
19191
19475
  if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
19192
19476
  return ok2(await this.listRemoteDirectory(
19193
19477
  value.targetDeviceId,
@@ -19196,38 +19480,38 @@ var ClientModeRuntime = class {
19196
19480
  ));
19197
19481
  }
19198
19482
  if (endpoint === "workspaces.list") {
19199
- const value = record2(payload);
19483
+ const value = record3(payload);
19200
19484
  if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
19201
19485
  return ok2(await this.listRemoteWorkspaces(value.targetDeviceId, signal));
19202
19486
  }
19203
19487
  if (endpoint === "codex.workspaces.list") {
19204
- const value = record2(payload);
19488
+ const value = record3(payload);
19205
19489
  if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
19206
19490
  return ok2(await this.listCodexWorkspaces(value.targetDeviceId, signal));
19207
19491
  }
19208
19492
  if (endpoint === "workspace.open") {
19209
- const value = record2(payload);
19493
+ const value = record3(payload);
19210
19494
  if (typeof value.targetDeviceId !== "string" || typeof value.path !== "string") {
19211
19495
  throw new ClientModeError("INVALID_MESSAGE", "A Host and working directory are required.");
19212
19496
  }
19213
19497
  return ok2(await this.openRemoteWorkspace(value.targetDeviceId, value.path, signal));
19214
19498
  }
19215
19499
  if (endpoint === "codex.workspace.open") {
19216
- const value = record2(payload);
19500
+ const value = record3(payload);
19217
19501
  if (typeof value.targetDeviceId !== "string" || typeof value.workspaceId !== "string") {
19218
19502
  throw new ClientModeError("INVALID_MESSAGE", "A Host and CodeX Workspace are required.");
19219
19503
  }
19220
19504
  return ok2(await this.openCodexWorkspace(value.targetDeviceId, value.workspaceId, signal));
19221
19505
  }
19222
19506
  if (endpoint === "codex.workspace.create") {
19223
- const value = record2(payload);
19507
+ const value = record3(payload);
19224
19508
  if (typeof value.targetDeviceId !== "string" || typeof value.path !== "string") {
19225
19509
  throw new ClientModeError("INVALID_MESSAGE", "A Host and CodeX project directory are required.");
19226
19510
  }
19227
19511
  return ok2(await this.createCodexWorkspace(value.targetDeviceId, value.path, signal));
19228
19512
  }
19229
19513
  if (endpoint === "workspace.selection.consume") {
19230
- const value = record2(payload);
19514
+ const value = record3(payload);
19231
19515
  if (typeof value.targetDeviceId !== "string" || typeof value.workspaceId !== "string") {
19232
19516
  throw new ClientModeError("INVALID_MESSAGE", "A Host and Workspace are required.");
19233
19517
  }
@@ -19243,7 +19527,7 @@ var ClientModeRuntime = class {
19243
19527
  return ok2(await this.callRemoteFileViewer(method, payload, signal));
19244
19528
  }
19245
19529
  if (endpoint === "codex.call") {
19246
- const value = record2(payload);
19530
+ const value = record3(payload);
19247
19531
  if (typeof value.method !== "string" || !("params" in value)) {
19248
19532
  throw new ClientModeError("INVALID_MESSAGE", "A Codex method and params are required.");
19249
19533
  }
@@ -19267,7 +19551,7 @@ var ClientModeRuntime = class {
19267
19551
  return ok2({ supported: local || remoteSupported, local, remote: remoteSupported });
19268
19552
  }
19269
19553
  if (endpoint === "codex.respond") {
19270
- const value = record2(payload);
19554
+ const value = record3(payload);
19271
19555
  const remote = this.activeCodexRemote();
19272
19556
  if (remote !== void 0) return ok2(await remote.client.rpc("codex.app.respond", value, signal));
19273
19557
  const host = this.requireLocalCodex();
@@ -19278,14 +19562,14 @@ var ClientModeRuntime = class {
19278
19562
  if (endpoint === "codex.stream.close") return ok2(await this.closeCodexStream(payload));
19279
19563
  if (endpoint === "host.account.login") {
19280
19564
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19281
- const value = record2(payload);
19565
+ const value = record3(payload);
19282
19566
  if (typeof value.email !== "string" || typeof value.password !== "string") {
19283
19567
  throw new ClientModeError("INVALID_MESSAGE", "Email and password are required.");
19284
19568
  }
19285
19569
  return ok2(await this.host.authorizeHostWithAccount(value.email, value.password));
19286
19570
  }
19287
19571
  if (endpoint === "host.authorization.set") {
19288
- const value = record2(payload);
19572
+ const value = record3(payload);
19289
19573
  if (typeof value.enabled !== "boolean") {
19290
19574
  throw new ClientModeError("INVALID_MESSAGE", "Host authorization state is required.");
19291
19575
  }
@@ -19293,14 +19577,14 @@ var ClientModeRuntime = class {
19293
19577
  }
19294
19578
  if (endpoint === "host.registration-code.submit") {
19295
19579
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19296
- const value = record2(payload);
19580
+ const value = record3(payload);
19297
19581
  if (typeof value.code !== "string" || value.code.trim() === "") {
19298
19582
  throw new ClientModeError("INVALID_MESSAGE", "A Host registration code is required.");
19299
19583
  }
19300
19584
  return ok2(await this.host.authorizeHostWithCode(value.code));
19301
19585
  }
19302
19586
  if (endpoint === "mode.set") {
19303
- const value = record2(payload);
19587
+ const value = record3(payload);
19304
19588
  if (value.mode !== "local" && value.mode !== "remote") throw new ClientModeError("INVALID_MESSAGE", "Mode must be local or remote.");
19305
19589
  return ok2(await this.setMode(value.mode, typeof value.targetDeviceId === "string" ? value.targetDeviceId : void 0, signal));
19306
19590
  }
@@ -19364,13 +19648,13 @@ function webrtcDiagnosticsLogFields(diagnostics) {
19364
19648
  ...diagnostics.selectedPath === void 0 ? {} : { rtcSelectedPath: diagnostics.selectedPath }
19365
19649
  };
19366
19650
  }
19367
- function record2(value) {
19651
+ function record3(value) {
19368
19652
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
19369
19653
  throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
19370
19654
  }
19371
19655
  return value;
19372
19656
  }
19373
- function isRecord5(value) {
19657
+ function isRecord6(value) {
19374
19658
  return typeof value === "object" && value !== null && !Array.isArray(value);
19375
19659
  }
19376
19660
  function ok2(value) {
@@ -19395,11 +19679,11 @@ async function readRemoteWorkspaceBaseline(gateway, signal) {
19395
19679
  const iterator = source[Symbol.asyncIterator]();
19396
19680
  try {
19397
19681
  const first = await iterator.next();
19398
- if (first.done || !isRecord5(first.value) || first.value.type !== "baseline" || !isRecord5(first.value.value) || !Array.isArray(first.value.value.items)) {
19682
+ if (first.done || !isRecord6(first.value) || first.value.type !== "baseline" || !isRecord6(first.value.value) || !Array.isArray(first.value.value.items)) {
19399
19683
  throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace baseline.");
19400
19684
  }
19401
19685
  return first.value.value.items.map((item) => {
19402
- if (!isRecord5(item) || typeof item.workspaceId !== "string" || typeof item.path !== "string" || typeof item.title !== "string") {
19686
+ if (!isRecord6(item) || typeof item.workspaceId !== "string" || typeof item.path !== "string" || typeof item.title !== "string") {
19403
19687
  throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace row.");
19404
19688
  }
19405
19689
  return { workspaceId: item.workspaceId, path: item.path, title: item.title };
@@ -19464,12 +19748,18 @@ async function probeRemoteHostFeatures(client, clientVersion) {
19464
19748
  if (error instanceof Error && "code" in error && error.code === "METHOD_NOT_FOUND") return fallback;
19465
19749
  throw error;
19466
19750
  }
19467
- if (!isRecord5(value) || !Array.isArray(value.capabilities) || value.capabilities.some((capability) => typeof capability !== "string")) {
19751
+ if (!isRecord6(value) || !Array.isArray(value.capabilities) || value.capabilities.some((capability) => typeof capability !== "string")) {
19468
19752
  throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned invalid transport capabilities.");
19469
19753
  }
19470
19754
  const capabilities = new Set(value.capabilities);
19471
19755
  const apiProxy = capabilities.has("harness.api.v1");
19472
- const remoteGateway = capabilities.has("harness.remote.v1");
19756
+ const remoteV1 = capabilities.has("harness.remote.v1");
19757
+ const remoteV3 = capabilities.has("harness.remote.v3");
19758
+ if (remoteV1 && remoteV3) {
19759
+ throw new ClientModeError("INVALID_MESSAGE", "The remote Host advertised conflicting Harness Session formats.");
19760
+ }
19761
+ const sessionFormat = remoteV3 ? 3 : void 0;
19762
+ const remoteGateway = remoteV3 || remoteV1;
19473
19763
  if (!apiProxy && !remoteGateway) {
19474
19764
  throw new ClientModeError("FEATURE_NOT_SUPPORTED", "The remote Host exposes no supported Harness transport.");
19475
19765
  }
@@ -19478,6 +19768,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
19478
19768
  fileViewer: capabilities.has("fileviewer.read.v1"),
19479
19769
  apiProxy,
19480
19770
  remoteGateway,
19771
+ ...sessionFormat === void 0 ? {} : { sessionFormat },
19481
19772
  codex: capabilities.has("codex.appserver.v1")
19482
19773
  };
19483
19774
  }
@@ -19528,10 +19819,6 @@ function preferredTransportsForAttempt(attempt) {
19528
19819
  if (attempt === "turn") return ["turn", "relay"];
19529
19820
  return ["relay"];
19530
19821
  }
19531
- function activeTransportsForAttempt(attempt) {
19532
- if (attempt === "direct") return ["lan", "p2p"];
19533
- return [attempt];
19534
- }
19535
19822
  function transportPreferenceForMode(mode) {
19536
19823
  if (mode === "LAN") return "lan";
19537
19824
  if (mode === "P2P") return "p2p";
@@ -19548,9 +19835,9 @@ import { hostname as hostname2 } from "node:os";
19548
19835
 
19549
19836
  // src/identity-store.ts
19550
19837
  import { createHash } from "node:crypto";
19551
- import { chmod, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
19838
+ import { chmod, mkdir, readFile as readFile2, rename, rm, stat, writeFile } from "node:fs/promises";
19552
19839
  import { homedir } from "node:os";
19553
- import { dirname as dirname2, join as join2 } from "node:path";
19840
+ import { dirname as dirname3, join as join3 } from "node:path";
19554
19841
  var identitySchema = external_exports.object({
19555
19842
  schemaVersion: external_exports.literal(1),
19556
19843
  deviceId: external_exports.string().uuid(),
@@ -19579,38 +19866,38 @@ var IdentityStore = class {
19579
19866
  peers = /* @__PURE__ */ new Map();
19580
19867
  constructor(options = {}) {
19581
19868
  const env = options.env ?? process.env;
19582
- const dshHome = env.DSH_HOME || join2(options.homeDirectory ?? homedir(), ".dsh");
19583
- this.directory = options.directory ?? join2(dshHome, "remote");
19869
+ const dshHome = env.DSH_HOME || join3(options.homeDirectory ?? homedir(), ".dsh");
19870
+ this.directory = options.directory ?? join3(dshHome, "remote");
19584
19871
  }
19585
19872
  async loadOrCreate(deviceName) {
19586
19873
  await mkdir(this.directory, { recursive: true, mode: 448 });
19587
19874
  await chmod(this.directory, 448);
19588
- const devicePath = join2(this.directory, "device.json");
19589
- const keyPath = join2(this.directory, "device.key");
19875
+ const devicePath = join3(this.directory, "device.json");
19876
+ const keyPath = join3(this.directory, "device.key");
19590
19877
  const [hasDevice, hasKey] = await Promise.all([exists(devicePath), exists(keyPath)]);
19591
19878
  if (hasDevice !== hasKey) {
19592
19879
  throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
19593
19880
  }
19594
19881
  if (!hasDevice) {
19595
19882
  const keys = generateKeyPair();
19596
- const record5 = { schemaVersion: 1, deviceId: uuidV7(), name: deviceName, publicKey: keys.publicKey };
19597
- await atomicJsonWrite(devicePath, record5, 384);
19883
+ const record6 = { schemaVersion: 1, deviceId: uuidV7(), name: deviceName, publicKey: keys.publicKey };
19884
+ await atomicJsonWrite(devicePath, record6, 384);
19598
19885
  await atomicTextWrite(keyPath, `${keys.privateKey}
19599
19886
  `, 384);
19600
19887
  }
19601
19888
  await assertPrivateMode(keyPath);
19602
19889
  try {
19603
- let record5 = identitySchema.parse(JSON.parse(await readFile(devicePath, "utf8")));
19604
- const privateKey = (await readFile(keyPath, "utf8")).trim();
19890
+ let record6 = identitySchema.parse(JSON.parse(await readFile2(devicePath, "utf8")));
19891
+ const privateKey = (await readFile2(keyPath, "utf8")).trim();
19605
19892
  const regenerated = generateKeyPair(fromBase64Url2(privateKey));
19606
- if (regenerated.publicKey !== record5.publicKey) {
19893
+ if (regenerated.publicKey !== record6.publicKey) {
19607
19894
  throw new IdentityInvalidError("device public and private keys do not match");
19608
19895
  }
19609
- if (record5.name !== deviceName) {
19610
- record5 = { ...record5, name: deviceName };
19611
- await atomicJsonWrite(devicePath, record5, 384);
19896
+ if (record6.name !== deviceName) {
19897
+ record6 = { ...record6, name: deviceName };
19898
+ await atomicJsonWrite(devicePath, record6, 384);
19612
19899
  }
19613
- this.identity = { ...record5, privateKey, fingerprint: fingerprint(record5.publicKey) };
19900
+ this.identity = { ...record6, privateKey, fingerprint: fingerprint(record6.publicKey) };
19614
19901
  await this.loadPeers();
19615
19902
  return this.identity;
19616
19903
  } catch (error) {
@@ -19655,11 +19942,11 @@ var IdentityStore = class {
19655
19942
  return removed;
19656
19943
  }
19657
19944
  async loadPeers() {
19658
- const path = join2(this.directory, "trusted-peers.json");
19945
+ const path = join3(this.directory, "trusted-peers.json");
19659
19946
  if (!await exists(path)) {
19660
19947
  await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
19661
19948
  }
19662
- const parsed = trustedPeersSchema.parse(JSON.parse(await readFile(path, "utf8")));
19949
+ const parsed = trustedPeersSchema.parse(JSON.parse(await readFile2(path, "utf8")));
19663
19950
  const peers = /* @__PURE__ */ new Map();
19664
19951
  for (const peer of parsed.peers) {
19665
19952
  if (peer.fingerprint !== fingerprint(peer.publicKey)) {
@@ -19671,7 +19958,7 @@ var IdentityStore = class {
19671
19958
  this.peers = peers;
19672
19959
  }
19673
19960
  async savePeers() {
19674
- await atomicJsonWrite(join2(this.directory, "trusted-peers.json"), {
19961
+ await atomicJsonWrite(join3(this.directory, "trusted-peers.json"), {
19675
19962
  schemaVersion: 1,
19676
19963
  peers: [...this.peers.values()]
19677
19964
  }, 384);
@@ -19680,7 +19967,7 @@ var IdentityStore = class {
19680
19967
  function serverStorageDirectory(root, serverUrl, role) {
19681
19968
  const origin = new URL(serverUrl).origin;
19682
19969
  const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
19683
- return join2(root, "servers", scope, role);
19970
+ return join3(root, "servers", scope, role);
19684
19971
  }
19685
19972
  function fingerprint(publicKey) {
19686
19973
  const compact = createHash("sha256").update(fromBase64Url2(publicKey)).digest("hex").slice(0, 12).toUpperCase();
@@ -19698,7 +19985,7 @@ async function atomicJsonWrite(path, value, mode) {
19698
19985
  `, mode);
19699
19986
  }
19700
19987
  async function atomicTextWrite(path, value, mode) {
19701
- await mkdir(dirname2(path), { recursive: true, mode: 448 });
19988
+ await mkdir(dirname3(path), { recursive: true, mode: 448 });
19702
19989
  const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
19703
19990
  await writeFile(temporary, value, { encoding: "utf8", mode, flag: "wx" });
19704
19991
  await chmod(temporary, mode);
@@ -19722,8 +20009,8 @@ function safeErrorMessage(error) {
19722
20009
  }
19723
20010
 
19724
20011
  // src/server-credentials.ts
19725
- import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
19726
- import { dirname as dirname3, join as join3 } from "node:path";
20012
+ import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
20013
+ import { dirname as dirname4, join as join4 } from "node:path";
19727
20014
  var credentialSchema = external_exports.object({
19728
20015
  schemaVersion: external_exports.literal(1),
19729
20016
  serverUrl: external_exports.string().url(),
@@ -19738,24 +20025,24 @@ var credentialSchema = external_exports.object({
19738
20025
  var ServerCredentialStore = class {
19739
20026
  path;
19740
20027
  constructor(directory) {
19741
- this.path = join3(directory, "server-credentials.json");
20028
+ this.path = join4(directory, "server-credentials.json");
19742
20029
  }
19743
20030
  async load(serverUrl, deviceId) {
19744
20031
  if (!await exists2(this.path)) return void 0;
19745
20032
  await assertPrivateMode2(this.path);
19746
20033
  let parsed;
19747
20034
  try {
19748
- parsed = credentialSchema.parse(JSON.parse(await readFile2(this.path, "utf8")));
20035
+ parsed = credentialSchema.parse(JSON.parse(await readFile3(this.path, "utf8")));
19749
20036
  } catch (error) {
19750
20037
  throw new ServerCredentialsInvalidError(`server credentials are invalid: ${safeMessage(error)}`);
19751
20038
  }
19752
20039
  return parsed.serverUrl === serverUrl && parsed.deviceId === deviceId ? parsed : void 0;
19753
20040
  }
19754
20041
  async save(credentials) {
19755
- const record5 = credentialSchema.parse({ schemaVersion: 1, ...credentials });
19756
- await atomicWrite(this.path, `${JSON.stringify(record5, null, 2)}
20042
+ const record6 = credentialSchema.parse({ schemaVersion: 1, ...credentials });
20043
+ await atomicWrite(this.path, `${JSON.stringify(record6, null, 2)}
19757
20044
  `);
19758
- return record5;
20045
+ return record6;
19759
20046
  }
19760
20047
  async clear() {
19761
20048
  await rm2(this.path, { force: true });
@@ -19765,7 +20052,7 @@ var ServerCredentialsInvalidError = class extends Error {
19765
20052
  code = "SERVER_CREDENTIALS_INVALID";
19766
20053
  };
19767
20054
  async function atomicWrite(path, contents) {
19768
- await mkdir2(dirname3(path), { recursive: true, mode: 448 });
20055
+ await mkdir2(dirname4(path), { recursive: true, mode: 448 });
19769
20056
  const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
19770
20057
  await writeFile2(temporary, contents, { encoding: "utf8", mode: 384, flag: "wx" });
19771
20058
  await chmod2(temporary, 384);
@@ -19822,7 +20109,7 @@ var PluginControlRuntime = class {
19822
20109
  if (endpoint === "devices") return ok3([]);
19823
20110
  if (endpoint === "host.account.login") {
19824
20111
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19825
- const value = record3(payload);
20112
+ const value = record4(payload);
19826
20113
  if (typeof value.email !== "string" || typeof value.password !== "string") {
19827
20114
  throw new ClientModeError("INVALID_MESSAGE", "Email and password are required.");
19828
20115
  }
@@ -19830,13 +20117,13 @@ var PluginControlRuntime = class {
19830
20117
  }
19831
20118
  if (endpoint === "host.registration-code.submit") {
19832
20119
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19833
- const value = record3(payload);
20120
+ const value = record4(payload);
19834
20121
  if (typeof value.code !== "string" || value.code.trim() === "") {
19835
20122
  throw new ClientModeError("INVALID_MESSAGE", "A Host registration code is required.");
19836
20123
  }
19837
20124
  return ok3(await this.host.authorizeHostWithCode(value.code));
19838
20125
  }
19839
- if (endpoint === "mode.set" && record3(payload).mode === "local") return ok3(this.hostOnlyStatus());
20126
+ if (endpoint === "mode.set" && record4(payload).mode === "local") return ok3(this.hostOnlyStatus());
19840
20127
  throw new ClientModeError("METHOD_NOT_ALLOWED", "Remote Client mode is disabled by the plugin role.");
19841
20128
  } catch (error) {
19842
20129
  return fail3(error);
@@ -19846,7 +20133,7 @@ var PluginControlRuntime = class {
19846
20133
  if (this.settings === void 0) {
19847
20134
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19848
20135
  }
19849
- const value = record3(payload);
20136
+ const value = record4(payload);
19850
20137
  if (value.role !== "host" && value.role !== "client") {
19851
20138
  throw new ClientModeError("INVALID_MESSAGE", "Role must be Host or Client.");
19852
20139
  }
@@ -19881,7 +20168,7 @@ var PluginControlRuntime = class {
19881
20168
  if (this.settings === void 0) {
19882
20169
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19883
20170
  }
19884
- const value = record3(payload);
20171
+ const value = record4(payload);
19885
20172
  if (typeof value.serverUrl !== "string") {
19886
20173
  throw new ClientModeError("INVALID_MESSAGE", "Server URL is required.");
19887
20174
  }
@@ -19894,7 +20181,7 @@ var PluginControlRuntime = class {
19894
20181
  if (this.settings === void 0) {
19895
20182
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19896
20183
  }
19897
- const role = record3(payload).role;
20184
+ const role = record4(payload).role;
19898
20185
  if (role !== "host" && role !== "client") {
19899
20186
  throw new ClientModeError("INVALID_MESSAGE", "Role must be Host or Client.");
19900
20187
  }
@@ -19910,7 +20197,7 @@ var PluginControlRuntime = class {
19910
20197
  if (this.settings === void 0) {
19911
20198
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19912
20199
  }
19913
- const enabled = record3(payload).enabled;
20200
+ const enabled = record4(payload).enabled;
19914
20201
  if (typeof enabled !== "boolean") {
19915
20202
  throw new ClientModeError("INVALID_MESSAGE", "Codex Remote enabled must be a boolean.");
19916
20203
  }
@@ -20016,11 +20303,11 @@ function editableConfig(config) {
20016
20303
  }
20017
20304
  };
20018
20305
  }
20019
- function isRecord6(value) {
20306
+ function isRecord7(value) {
20020
20307
  return typeof value === "object" && value !== null && !Array.isArray(value);
20021
20308
  }
20022
- function record3(value) {
20023
- if (!isRecord6(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
20309
+ function record4(value) {
20310
+ if (!isRecord7(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
20024
20311
  return value;
20025
20312
  }
20026
20313
  function ok3(value) {
@@ -20243,36 +20530,6 @@ function shortId2(value) {
20243
20530
  return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
20244
20531
  }
20245
20532
 
20246
- // src/harness-version.ts
20247
- import { readFile as readFile3 } from "node:fs/promises";
20248
- import { dirname as dirname4, isAbsolute, join as join4 } from "node:path";
20249
- var LEGACY_PLACEHOLDER_VERSION = "0.0.1";
20250
- function normalizeHarnessVersion(value) {
20251
- if (typeof value !== "string") return void 0;
20252
- const version = value.trim();
20253
- if (version.length === 0 || version.length > 64 || /[\u0000-\u001f]/u.test(version)) return void 0;
20254
- return version;
20255
- }
20256
- function selectHarnessVersion(reportedVersion, distributionVersion) {
20257
- if (reportedVersion !== void 0 && reportedVersion !== LEGACY_PLACEHOLDER_VERSION) return reportedVersion;
20258
- return distributionVersion;
20259
- }
20260
- async function readHarnessDistributionVersion(entrypoint = process.argv[1]) {
20261
- if (entrypoint === void 0 || !isAbsolute(entrypoint)) return void 0;
20262
- let directory = dirname4(entrypoint);
20263
- for (let depth = 0; depth < 8; depth += 1) {
20264
- try {
20265
- const manifest = JSON.parse(await readFile3(join4(directory, "package.json"), "utf8"));
20266
- if (manifest.name === "@deepseek-ai/dsh") return normalizeHarnessVersion(manifest.version);
20267
- } catch {
20268
- }
20269
- const parent = dirname4(directory);
20270
- if (parent === directory) break;
20271
- directory = parent;
20272
- }
20273
- return void 0;
20274
- }
20275
-
20276
20533
  // src/safe-error.ts
20277
20534
  var RpcError = class extends Error {
20278
20535
  constructor(code, message, details, retryable = false) {
@@ -22626,14 +22883,14 @@ function needsDirectoryFallback(result) {
22626
22883
  return code.includes("capability") || code === "directory-picker/unavailable" || code === "directory-picker-unavailable" || message.includes("browse capability") || message.includes("browser capability") || message.includes("brower capability") || message.includes("directory-picker-unavailable");
22627
22884
  }
22628
22885
  function requestArgs(payload) {
22629
- const root = record4(payload);
22630
- const args = isRecord7(root.args) ? root.args : root;
22631
- return record4(args.request ?? args._request ?? args);
22886
+ const root = record5(payload);
22887
+ const args = isRecord8(root.args) ? root.args : root;
22888
+ return record5(args.request ?? args._request ?? args);
22632
22889
  }
22633
- function record4(value) {
22634
- return isRecord7(value) ? value : {};
22890
+ function record5(value) {
22891
+ return isRecord8(value) ? value : {};
22635
22892
  }
22636
- function isRecord7(value) {
22893
+ function isRecord8(value) {
22637
22894
  return typeof value === "object" && value !== null && !Array.isArray(value);
22638
22895
  }
22639
22896
 
@@ -22827,7 +23084,7 @@ var CodexAppServerClient = class {
22827
23084
  this.handleProcessFailure("CODEX_INVALID_RESPONSE", new Error("Codex App Server emitted invalid JSON."));
22828
23085
  return;
22829
23086
  }
22830
- if (!isRecord8(value)) {
23087
+ if (!isRecord9(value)) {
22831
23088
  this.handleProcessFailure("CODEX_INVALID_RESPONSE", new Error("Codex App Server emitted an invalid message."));
22832
23089
  return;
22833
23090
  }
@@ -22872,12 +23129,12 @@ var CodexAppServerClient = class {
22872
23129
  }
22873
23130
  };
22874
23131
  function safeUpstreamError(value) {
22875
- if (!isRecord8(value) || typeof value.message !== "string") return "Codex App Server rejected the request.";
23132
+ if (!isRecord9(value) || typeof value.message !== "string") return "Codex App Server rejected the request.";
22876
23133
  const message = value.message.toLowerCase();
22877
23134
  if (message.includes("active writer")) return "Codex thread already has an active writer.";
22878
23135
  return message.includes("not initialized") ? "Codex App Server is not initialized." : "Codex App Server rejected the request.";
22879
23136
  }
22880
- function isRecord8(value) {
23137
+ function isRecord9(value) {
22881
23138
  return typeof value === "object" && value !== null && !Array.isArray(value);
22882
23139
  }
22883
23140
 
@@ -23247,15 +23504,15 @@ function decodeCanonicalBase643(value) {
23247
23504
  }
23248
23505
  return decoded;
23249
23506
  }
23250
- function isRecord9(value) {
23507
+ function isRecord10(value) {
23251
23508
  return typeof value === "object" && value !== null && !Array.isArray(value);
23252
23509
  }
23253
23510
  function safeErrorCode2(error) {
23254
- if (isRecord9(error) && typeof error.code === "string") return error.code;
23511
+ if (isRecord10(error) && typeof error.code === "string") return error.code;
23255
23512
  return "UNKNOWN";
23256
23513
  }
23257
23514
  function safeMethod(input2) {
23258
- return isRecord9(input2) && typeof input2.method === "string" ? input2.method : "invalid";
23515
+ return isRecord10(input2) && typeof input2.method === "string" ? input2.method : "invalid";
23259
23516
  }
23260
23517
  function concatChunks3(chunks, totalBytes) {
23261
23518
  const output = new Uint8Array(totalBytes);
@@ -23289,6 +23546,7 @@ var CodexRemoteDomain = class {
23289
23546
  peerDeviceIds = /* @__PURE__ */ new Map();
23290
23547
  turnOwners = /* @__PURE__ */ new Map();
23291
23548
  approvals = /* @__PURE__ */ new Map();
23549
+ permissionPresets = /* @__PURE__ */ new Map();
23292
23550
  approvalExpiryTimer;
23293
23551
  restartTimer;
23294
23552
  restartAttempt = 0;
@@ -23367,6 +23625,7 @@ var CodexRemoteDomain = class {
23367
23625
  if (extractThread(result)?.id === void 0) {
23368
23626
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread.");
23369
23627
  }
23628
+ this.rememberPermission(extractThread(result).id, result);
23370
23629
  return result;
23371
23630
  }
23372
23631
  const threadId = threadIdFromParams(call.params);
@@ -23381,12 +23640,23 @@ var CodexRemoteDomain = class {
23381
23640
  }
23382
23641
  );
23383
23642
  const activeTurnId2 = typeof page.activeTurnId === "string" ? page.activeTurnId : this.turnOwners.get(threadId)?.turnId;
23384
- return activeTurnId2 === void 0 ? page : { ...page, activeTurnId: activeTurnId2 };
23643
+ return {
23644
+ ...page,
23645
+ ...activeTurnId2 === void 0 ? {} : { activeTurnId: activeTurnId2 },
23646
+ permissionPreset: this.permissionPresets.get(threadId) ?? null
23647
+ };
23385
23648
  }
23386
23649
  if (call.method === "dsh/directoryList") {
23387
23650
  return this.listCodexDirectory(call.params.path);
23388
23651
  }
23389
23652
  const allowedThread = threadId === void 0 ? void 0 : await this.readKnownThread(threadId);
23653
+ if (call.method === "thread/resume" && call.params.permissionPreset !== void 0) {
23654
+ const owner = this.turnOwners.get(threadId);
23655
+ if (owner !== void 0 && owner.connectionId !== connectionId) {
23656
+ throw new RpcError("CODEX_THREAD_BUSY", "Another Remote client is writing to this CodeX thread.");
23657
+ }
23658
+ return this.changeThreadPermission(threadId, call.params, allowedThread);
23659
+ }
23390
23660
  if (call.method === "thread/read") {
23391
23661
  return this.callUpstream(call.method, call.params);
23392
23662
  }
@@ -23436,8 +23706,18 @@ var CodexRemoteDomain = class {
23436
23706
  if (call.method === "thread/resume" || call.method === "thread/fork" || call.method === "thread/unarchive") {
23437
23707
  await this.assertResultThreadAllowed(result);
23438
23708
  }
23709
+ if (call.method === "thread/resume" || call.method === "thread/fork") {
23710
+ const resultId = extractThread(result)?.id;
23711
+ if (typeof resultId === "string") {
23712
+ this.rememberPermission(resultId, result);
23713
+ if (call.params.permissionPreset !== void 0) await this.publishPermission(resultId, result);
23714
+ }
23715
+ }
23439
23716
  if (call.method === "turn/start" && threadId !== void 0) {
23440
23717
  this.rememberTurnId(threadId, connectionId, extractTurnId(result));
23718
+ if (call.params.permissionPreset !== void 0) {
23719
+ await this.publishPermission(threadId, codexTurnPermissionParams(permission, allowedThread?.cwd));
23720
+ }
23441
23721
  }
23442
23722
  return result;
23443
23723
  } catch (error) {
@@ -23486,6 +23766,7 @@ var CodexRemoteDomain = class {
23486
23766
  this.peers.clear();
23487
23767
  this.peerDeviceIds.clear();
23488
23768
  this.turnOwners.clear();
23769
+ this.permissionPresets.clear();
23489
23770
  if (this.approvalExpiryTimer !== void 0) clearTimeout(this.approvalExpiryTimer);
23490
23771
  this.approvalExpiryTimer = void 0;
23491
23772
  this.approvals.clear();
@@ -23548,6 +23829,7 @@ var CodexRemoteDomain = class {
23548
23829
  this.state = "restarting";
23549
23830
  this.unavailableCode = code;
23550
23831
  this.turnOwners.clear();
23832
+ this.permissionPresets.clear();
23551
23833
  this.approvals.clear();
23552
23834
  if (this.approvalExpiryTimer !== void 0) clearTimeout(this.approvalExpiryTimer);
23553
23835
  this.approvalExpiryTimer = void 0;
@@ -23603,6 +23885,10 @@ var CodexRemoteDomain = class {
23603
23885
  return;
23604
23886
  }
23605
23887
  const threadId = extractThreadId(message.params);
23888
+ if (message.method === "thread/settings/updated" && threadId !== void 0) {
23889
+ this.rememberPermission(threadId, message.params);
23890
+ }
23891
+ if (message.method === "thread/closed" && threadId !== void 0) this.permissionPresets.delete(threadId);
23606
23892
  if (message.method === "turn/completed" && threadId !== void 0) this.turnOwners.delete(threadId);
23607
23893
  if (message.method === "turn/started" && threadId !== void 0) {
23608
23894
  const owner = this.turnOwners.get(threadId);
@@ -23618,6 +23904,42 @@ var CodexRemoteDomain = class {
23618
23904
  params: message.params
23619
23905
  })));
23620
23906
  }
23907
+ rememberPermission(threadId, value) {
23908
+ const preset = codexPermissionPresetFromResponse(value);
23909
+ if (preset === void 0) this.permissionPresets.delete(threadId);
23910
+ else this.permissionPresets.set(threadId, preset);
23911
+ }
23912
+ async changeThreadPermission(threadId, params, allowed) {
23913
+ const permission = codexPermission(params);
23914
+ const settings = {
23915
+ ...codexTurnPermissionParams(permission, allowed.cwd),
23916
+ ...params.model === void 0 ? {} : { model: params.model }
23917
+ };
23918
+ let result;
23919
+ try {
23920
+ await this.callUpstream("thread/settings/update", { threadId, ...settings });
23921
+ result = { thread: allowed.thread, ...settings, sandbox: settings.sandboxPolicy };
23922
+ } catch {
23923
+ result = await this.callUpstream("thread/resume", {
23924
+ ...permission.params,
23925
+ ...allowed.cwd === void 0 ? {} : { cwd: allowed.cwd },
23926
+ ...codexThreadPermissionParams(permission),
23927
+ excludeTurns: true
23928
+ });
23929
+ await this.assertResultThreadAllowed(result);
23930
+ if (codexPermissionPresetFromResponse(result) !== params.permissionPreset) {
23931
+ await this.callUpstream("thread/settings/update", { threadId, ...settings });
23932
+ result = { ...isRecord11(result) ? result : {}, ...settings, sandbox: settings.sandboxPolicy };
23933
+ }
23934
+ }
23935
+ await this.publishPermission(threadId, result);
23936
+ return result;
23937
+ }
23938
+ async publishPermission(threadId, value) {
23939
+ const source = isRecord11(value) ? value : {};
23940
+ const threadSettings = { approvalPolicy: source.approvalPolicy, sandboxPolicy: source.sandboxPolicy ?? source.sandbox };
23941
+ await this.handleInbound({ kind: "notification", method: "thread/settings/updated", params: { threadId, threadSettings } });
23942
+ }
23621
23943
  async handleServerRequest(message) {
23622
23944
  const appServer = this.requireAppServer();
23623
23945
  await this.expireApprovals();
@@ -23710,9 +24032,9 @@ var CodexRemoteDomain = class {
23710
24032
  itemsView,
23711
24033
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23712
24034
  });
23713
- const pageResult = isRecord10(result) ? result : {};
24035
+ const pageResult = isRecord11(result) ? result : {};
23714
24036
  for (const rawTurn of array2(pageResult.data)) {
23715
- if (!isRecord10(rawTurn)) continue;
24037
+ if (!isRecord11(rawTurn)) continue;
23716
24038
  const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
23717
24039
  const items = rawTurn.itemsView === "full" || turnId === void 0 ? array2(rawTurn.items) : await this.readThreadItems(connectionId, threadId, turnId, array2(rawTurn.items));
23718
24040
  turns.push({ ...rawTurn, items });
@@ -23734,9 +24056,9 @@ var CodexRemoteDomain = class {
23734
24056
  sortDirection: "asc",
23735
24057
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23736
24058
  });
23737
- const pageResult = isRecord10(result) ? result : {};
24059
+ const pageResult = isRecord11(result) ? result : {};
23738
24060
  for (const entry of array2(pageResult.data)) {
23739
- if (isRecord10(entry) && entry.item !== void 0) items.push(entry.item);
24061
+ if (isRecord11(entry) && entry.item !== void 0) items.push(entry.item);
23740
24062
  }
23741
24063
  cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
23742
24064
  if (cursor2 === void 0) break;
@@ -23800,7 +24122,7 @@ var CodexRemoteDomain = class {
23800
24122
  return [...this.peers.values()].some((peer) => peer.hasThreadSubscription(threadId));
23801
24123
  }
23802
24124
  resolveUpstreamApproval(params) {
23803
- if (!isRecord10(params) || typeof params.requestId !== "string" && typeof params.requestId !== "number") return;
24125
+ if (!isRecord11(params) || typeof params.requestId !== "string" && typeof params.requestId !== "number") return;
23804
24126
  for (const [handle, approval] of this.approvals) {
23805
24127
  if (approval.upstreamId === params.requestId) this.approvals.delete(handle);
23806
24128
  }
@@ -24017,24 +24339,24 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
24017
24339
  ])];
24018
24340
  }
24019
24341
  function parseCallEnvelope(input2) {
24020
- if (!isRecord10(input2) || typeof input2.method !== "string" || !("params" in input2) || Object.keys(input2).some((key) => key !== "method" && key !== "params")) {
24342
+ if (!isRecord11(input2) || typeof input2.method !== "string" || !("params" in input2) || Object.keys(input2).some((key) => key !== "method" && key !== "params")) {
24021
24343
  throw new RpcError("INVALID_MESSAGE", "The Codex call envelope is invalid.");
24022
24344
  }
24023
24345
  return { method: input2.method, params: input2.params };
24024
24346
  }
24025
24347
  function parseRespond(input2) {
24026
- if (!isRecord10(input2) || typeof input2.requestHandle !== "string" || !["accept", "decline", "cancel"].includes(String(input2.decision)) || Object.keys(input2).some((key) => key !== "requestHandle" && key !== "decision")) {
24348
+ if (!isRecord11(input2) || typeof input2.requestHandle !== "string" || !["accept", "decline", "cancel"].includes(String(input2.decision)) || Object.keys(input2).some((key) => key !== "requestHandle" && key !== "decision")) {
24027
24349
  throw new RpcError("INVALID_MESSAGE", "The Codex approval response is invalid.");
24028
24350
  }
24029
24351
  return input2;
24030
24352
  }
24031
24353
  function accountCanRun(result) {
24032
- if (!isRecord10(result) || typeof result.requiresOpenaiAuth !== "boolean") return false;
24033
- return result.requiresOpenaiAuth === false || isRecord10(result.account);
24354
+ if (!isRecord11(result) || typeof result.requiresOpenaiAuth !== "boolean") return false;
24355
+ return result.requiresOpenaiAuth === false || isRecord11(result.account);
24034
24356
  }
24035
24357
  function sanitizeAccount(result) {
24036
- if (!isRecord10(result)) throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned invalid account state.");
24037
- const account = isRecord10(result.account) ? result.account : void 0;
24358
+ if (!isRecord11(result)) throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned invalid account state.");
24359
+ const account = isRecord11(result.account) ? result.account : void 0;
24038
24360
  return {
24039
24361
  authenticated: account !== void 0 || result.requiresOpenaiAuth === false,
24040
24362
  requiresOpenaiAuth: result.requiresOpenaiAuth === true,
@@ -24047,11 +24369,11 @@ function sanitizeAccount(result) {
24047
24369
  };
24048
24370
  }
24049
24371
  function sanitizeThreadList(result) {
24050
- if (!isRecord10(result) || !Array.isArray(result.data)) {
24372
+ if (!isRecord11(result) || !Array.isArray(result.data)) {
24051
24373
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
24052
24374
  }
24053
24375
  const data = result.data.flatMap((value) => {
24054
- if (!isRecord10(value) || typeof value.id !== "string") return [];
24376
+ if (!isRecord11(value) || typeof value.id !== "string") return [];
24055
24377
  return [{
24056
24378
  id: value.id,
24057
24379
  ...typeof value.sessionId === "string" ? { sessionId: value.sessionId } : {},
@@ -24063,7 +24385,7 @@ function sanitizeThreadList(result) {
24063
24385
  ...typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) ? { updatedAt: value.updatedAt } : {},
24064
24386
  ...typeof value.archived === "boolean" ? { archived: value.archived } : {},
24065
24387
  ...typeof value.isPinned === "boolean" ? { isPinned: value.isPinned } : {},
24066
- ...isRecord10(value.status) ? { status: value.status } : {}
24388
+ ...isRecord11(value.status) ? { status: value.status } : {}
24067
24389
  }];
24068
24390
  });
24069
24391
  return {
@@ -24073,18 +24395,18 @@ function sanitizeThreadList(result) {
24073
24395
  };
24074
24396
  }
24075
24397
  function filterThreadListByWorkspaceAuthority(result, authority) {
24076
- if (!isRecord10(result) || !Array.isArray(result.data)) {
24398
+ if (!isRecord11(result) || !Array.isArray(result.data)) {
24077
24399
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
24078
24400
  }
24079
24401
  return {
24080
24402
  ...result,
24081
- data: result.data.map((record5) => isRecord10(record5) ? record5 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
24403
+ data: result.data.map((record6) => isRecord11(record6) ? record6 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
24082
24404
  };
24083
24405
  }
24084
24406
  function sanitizeProject(value) {
24085
- if (!isRecord10(value) || typeof value.id !== "string" || typeof value.name !== "string") return void 0;
24407
+ if (!isRecord11(value) || typeof value.id !== "string" || typeof value.name !== "string") return void 0;
24086
24408
  const roots = Array.isArray(value.roots) ? value.roots.flatMap((root) => {
24087
- const path = isRecord10(root) && typeof root.path === "string" && root.path.length > 0 ? root.path : void 0;
24409
+ const path = isRecord11(root) && typeof root.path === "string" && root.path.length > 0 ? root.path : void 0;
24088
24410
  return path === void 0 || !isAbsolute3(path) ? [] : [{ path }];
24089
24411
  }) : [];
24090
24412
  if (roots.length === 0) return void 0;
@@ -24098,7 +24420,7 @@ function sanitizeProject(value) {
24098
24420
  };
24099
24421
  }
24100
24422
  function sanitizeProjectList(result) {
24101
- if (!isRecord10(result) || !Array.isArray(result.data)) {
24423
+ if (!isRecord11(result) || !Array.isArray(result.data)) {
24102
24424
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid project list.");
24103
24425
  }
24104
24426
  const data = result.data.flatMap((value) => sanitizeProject(value) ?? []);
@@ -24108,7 +24430,7 @@ function sanitizeProjectList(result) {
24108
24430
  };
24109
24431
  }
24110
24432
  function sanitizeProjectCreate(result) {
24111
- const project = isRecord10(result) ? sanitizeProject(result.project) : void 0;
24433
+ const project = isRecord11(result) ? sanitizeProject(result.project) : void 0;
24112
24434
  if (project === void 0) {
24113
24435
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid created project.");
24114
24436
  }
@@ -24140,19 +24462,19 @@ function codexDirectoryCrumbs(root, path) {
24140
24462
  return crumbs2;
24141
24463
  }
24142
24464
  function extractThread(result) {
24143
- return isRecord10(result) && isRecord10(result.thread) ? result.thread : void 0;
24465
+ return isRecord11(result) && isRecord11(result.thread) ? result.thread : void 0;
24144
24466
  }
24145
24467
  function extractTurnId(result) {
24146
- if (!isRecord10(result)) return void 0;
24468
+ if (!isRecord11(result)) return void 0;
24147
24469
  if (typeof result.turnId === "string" && result.turnId.length > 0) return result.turnId;
24148
- if (isRecord10(result.turn) && typeof result.turn.id === "string" && result.turn.id.length > 0) return result.turn.id;
24470
+ if (isRecord11(result.turn) && typeof result.turn.id === "string" && result.turn.id.length > 0) return result.turn.id;
24149
24471
  return void 0;
24150
24472
  }
24151
24473
  function extractThreadId(params) {
24152
- if (!isRecord10(params)) return void 0;
24474
+ if (!isRecord11(params)) return void 0;
24153
24475
  if (typeof params.threadId === "string") return params.threadId;
24154
- if (isRecord10(params.thread) && typeof params.thread.id === "string") return params.thread.id;
24155
- if (isRecord10(params.turn) && typeof params.turn.threadId === "string") return params.turn.threadId;
24476
+ if (isRecord11(params.thread) && typeof params.thread.id === "string") return params.thread.id;
24477
+ if (isRecord11(params.turn) && typeof params.turn.threadId === "string") return params.turn.threadId;
24156
24478
  return void 0;
24157
24479
  }
24158
24480
  function optionalInteger2(value) {
@@ -24194,13 +24516,13 @@ function mapCodexImageInputs(params) {
24194
24516
  return {
24195
24517
  ...params,
24196
24518
  input: params.input.map((value) => {
24197
- if (!isRecord10(value) || value.type !== "image" || typeof value.mediaType !== "string" || typeof value.data !== "string") return value;
24519
+ if (!isRecord11(value) || value.type !== "image" || typeof value.mediaType !== "string" || typeof value.data !== "string") return value;
24198
24520
  return { type: "image", url: `data:${value.mediaType};base64,${value.data}` };
24199
24521
  })
24200
24522
  };
24201
24523
  }
24202
24524
  function sanitizeApprovalParams(params, requestHandle) {
24203
- if (!isRecord10(params)) return { requestHandle };
24525
+ if (!isRecord11(params)) return { requestHandle };
24204
24526
  const safe = { ...params };
24205
24527
  delete safe.proposedExecpolicyAmendment;
24206
24528
  delete safe.additionalPermissions;
@@ -24231,7 +24553,7 @@ function isProjectListFallbackError(error) {
24231
24553
  function canTryNextBinary(error) {
24232
24554
  return !(error instanceof RpcError) || !["CODEX_AUTH_REQUIRED", "CODEX_CLOSED"].includes(error.code);
24233
24555
  }
24234
- function isRecord10(value) {
24556
+ function isRecord11(value) {
24235
24557
  return typeof value === "object" && value !== null && !Array.isArray(value);
24236
24558
  }
24237
24559
  function array2(value) {
@@ -24339,6 +24661,9 @@ var HostPluginRuntime = class {
24339
24661
  accountRequired: error === "ACCOUNT_AUTH_REQUIRED" || error === "AUTH_INVALID" || error === "TOKEN_EXPIRED"
24340
24662
  };
24341
24663
  }
24664
+ localHarnessVersion() {
24665
+ return this.harnessVersion;
24666
+ }
24342
24667
  reconnectHost() {
24343
24668
  if (this.closed) throw new Error("remote runtime is closed");
24344
24669
  if (this.serverConnection === void 0) {
@@ -24508,9 +24833,13 @@ var HostPluginRuntime = class {
24508
24833
  }
24509
24834
  hostCapabilities() {
24510
24835
  const capabilities = [];
24511
- if (this.apiProxy !== void 0) capabilities.push("harness.api.v1", "harness.api.transfer.v1");
24512
24836
  if (this.localGateway?.supportsCarrier === true) {
24513
- capabilities.push("harness.remote.v1", "harness.remote.transfer.v1");
24837
+ capabilities.push(
24838
+ harnessSessionGeneration(this.harnessVersion) === "v3" ? "harness.remote.v3" : "harness.remote.v1",
24839
+ "harness.remote.transfer.v1"
24840
+ );
24841
+ } else if (this.apiProxy !== void 0) {
24842
+ capabilities.push("harness.api.v1", "harness.api.transfer.v1");
24514
24843
  }
24515
24844
  if (this.fileViewerHost?.() !== void 0) capabilities.push("fileviewer.read.v1");
24516
24845
  if (this.codex.isAvailable()) capabilities.push("codex.appserver.v1", "codex.appserver.transfer.v1");