ds-harness-remote 0.4.10 → 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.
package/dist/index.js CHANGED
@@ -13977,8 +13977,51 @@ function uuidV7(now = Date.now()) {
13977
13977
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
13978
13978
  }
13979
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
+
13980
14019
  // src/remote-api-proxy.ts
13981
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
+ };
13982
14025
  var RemoteHarnessApiProxy = class {
13983
14026
  constructor(client) {
13984
14027
  this.client = client;
@@ -14067,7 +14110,7 @@ var RemoteHarnessApiProxy = class {
14067
14110
  const params = {
14068
14111
  method,
14069
14112
  rpcId: String(request.rpcId),
14070
- payload: request.payload
14113
+ payload: normalizeLegacyRequest(method, request.payload)
14071
14114
  };
14072
14115
  const encoded = new TextEncoder().encode(JSON.stringify(params));
14073
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);
@@ -14179,9 +14222,32 @@ function normalizeLegacyResponse(method, response) {
14179
14222
  }
14180
14223
  };
14181
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
+ }
14182
14245
  function isRemoteDisconnect(error) {
14183
14246
  return error instanceof RemoteClientError && (error.code === "TRANSPORT_CLOSED" || error.code === "CLIENT_CLOSED");
14184
14247
  }
14248
+ function isRecord2(value) {
14249
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14250
+ }
14185
14251
  var AsyncFrameQueue = class {
14186
14252
  values = [];
14187
14253
  waiters = [];
@@ -14416,16 +14482,16 @@ function routeStreamEvent2(event, streamId, queue) {
14416
14482
  queue.fail(remoteFailure({
14417
14483
  code: typeof failure2?.code === "string" ? failure2.code : "internal",
14418
14484
  message: typeof failure2?.message === "string" ? failure2.message : "The remote Harness stream failed.",
14419
- details: isRecord2(failure2?.details) ? failure2.details : {}
14485
+ details: isRecord3(failure2?.details) ? failure2.details : {}
14420
14486
  }));
14421
14487
  } else {
14422
14488
  queue.close();
14423
14489
  }
14424
14490
  }
14425
14491
  function parseRpcResult(value) {
14426
- 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.");
14427
14493
  if (value.ok === true) return Object.hasOwn(value, "value") ? { ok: true, value: value.value } : { ok: true };
14428
- 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)) {
14429
14495
  throw new Error("The remote Host returned an invalid Gateway failure.");
14430
14496
  }
14431
14497
  return {
@@ -14459,7 +14525,7 @@ function base64ToBytes3(value) {
14459
14525
  if (bytesToBase643(bytes) !== value) throw new Error("The remote Host returned non-canonical Harness Remote transfer data.");
14460
14526
  return bytes;
14461
14527
  }
14462
- function isRecord2(value) {
14528
+ function isRecord3(value) {
14463
14529
  return typeof value === "object" && value !== null && !Array.isArray(value);
14464
14530
  }
14465
14531
  function hasErrorCode(error, code) {
@@ -14510,9 +14576,10 @@ async function discoverCodexVirtualWorkspaces(client, signal) {
14510
14576
  return (await loadCatalog(client, signal)).workspaces;
14511
14577
  }
14512
14578
  var CodexVirtualHarness = class _CodexVirtualHarness {
14513
- constructor(client, host) {
14579
+ constructor(client, host, sessionGeneration = "legacy") {
14514
14580
  this.client = client;
14515
14581
  this.host = host;
14582
+ this.sessionGeneration = sessionGeneration;
14516
14583
  this.api = this.createApiProxy();
14517
14584
  }
14518
14585
  api;
@@ -14536,8 +14603,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14536
14603
  commandSeq = 0;
14537
14604
  selectedWorkspaceId;
14538
14605
  closed = false;
14539
- static remote(core, host) {
14540
- return new _CodexVirtualHarness(new CodexRemoteClient(core), host);
14606
+ static remote(core, host, sessionGeneration = "legacy") {
14607
+ return new _CodexVirtualHarness(new CodexRemoteClient(core), host, sessionGeneration);
14541
14608
  }
14542
14609
  async workspaces(signal) {
14543
14610
  return (await this.refreshCatalog(signal)).workspaces;
@@ -14927,6 +14994,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14927
14994
  streamedBlocks: /* @__PURE__ */ new Map(),
14928
14995
  nextBlockIndex: 0,
14929
14996
  streamActive: false,
14997
+ assistantStreamRevision: 0,
14930
14998
  liveItems: /* @__PURE__ */ new Map(),
14931
14999
  liveToolOutput: /* @__PURE__ */ new Map(),
14932
15000
  liveToolResultSeq: /* @__PURE__ */ new Map(),
@@ -14950,7 +15018,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14950
15018
  permissions: codexPermissionsProjection(this.permissionSelection(sessionId)),
14951
15019
  imageLimits: codexImageLimitsProjection()
14952
15020
  }
14953
- }
15021
+ },
15022
+ ...this.sessionGeneration === "v3" ? { assistantStream: { revision: 0 } } : {}
14954
15023
  });
14955
15024
  try {
14956
15025
  const stream = await this.client.subscribe(
@@ -14988,6 +15057,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14988
15057
  }
14989
15058
  if (frame.method === "turn/started") {
14990
15059
  const turn = record2(params.turn);
15060
+ this.endAssistantAttempt(follow, { kind: "abandoned" });
14991
15061
  this.resetLiveTurn(follow);
14992
15062
  follow.turn += 1;
14993
15063
  follow.activeTurnId = string(turn.id);
@@ -15103,6 +15173,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15103
15173
  if (follow.stepOpen) {
15104
15174
  this.closeAllStreamBlocks(follow);
15105
15175
  if (follow.streamActive) this.finishStream(follow);
15176
+ this.endAssistantAttempt(follow, { kind: "abandoned" });
15106
15177
  this.pushEvent(follow, "step/end", { turn: follow.turn, step: 1 });
15107
15178
  follow.stepOpen = false;
15108
15179
  }
@@ -15141,6 +15212,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15141
15212
  this.closeItemStreamBlocks(follow, itemId, itemText2(item));
15142
15213
  if (type === "agentMessage" && follow.streamActive && follow.streamedBlocks.size === 0) this.finishStream(follow);
15143
15214
  const supplementalImages = type === "agentMessage" ? follow.pendingToolImages : [];
15215
+ let assistantMessageSeq;
15144
15216
  for (const event of itemEvents(
15145
15217
  item,
15146
15218
  follow.turn,
@@ -15152,7 +15224,17 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15152
15224
  )) {
15153
15225
  if (event.type === "tool/call" && follow.startedItems.has(itemId)) continue;
15154
15226
  if (event.type === "tool/result") this.pushToolResult(follow, itemId, event);
15155
- 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
+ });
15156
15238
  }
15157
15239
  if (type === "agentMessage" && supplementalImages.length > 0) follow.pendingToolImages = [];
15158
15240
  else if (type !== void 0 && isToolItemType2(type)) {
@@ -15169,18 +15251,21 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15169
15251
  type,
15170
15252
  seq,
15171
15253
  time: Date.now(),
15172
- 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
+ ),
15173
15259
  ...isSurfaceEvent(type) ? placement === void 0 ? { surfaceOp: "append" } : {
15174
- 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 },
15175
15261
  sourceEventSeqs: [placement.replaceSeq]
15176
15262
  } : {}
15177
15263
  };
15178
15264
  this.cacheImageBlocks(follow.sessionId, event.data);
15179
15265
  if (!follow.rcOnly) {
15180
- const alphaEvent = typeof event.surfaceOp === "object" ? { ...event, surfaceOp: "replace" } : event;
15181
15266
  follow.queue.push({
15182
15267
  type: "event",
15183
- event: alphaEvent,
15268
+ event,
15184
15269
  ...view === void 0 ? {} : { view }
15185
15270
  });
15186
15271
  }
@@ -15207,52 +15292,105 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15207
15292
  const block = { itemId, index: follow.nextBlockIndex++, kind, text: "" };
15208
15293
  follow.streamedBlocks.set(key, block);
15209
15294
  follow.streamActive = true;
15210
- this.pushEvent(follow, "assistant/chunk", {
15211
- turn: follow.turn,
15212
- step: 1,
15213
- chunk: { type: "block-start", index: block.index, blockType: kind }
15214
- });
15295
+ this.pushAssistantChunk(follow, { type: "block-start", index: block.index, blockType: kind });
15215
15296
  return block;
15216
15297
  }
15217
15298
  appendStreamDelta(follow, key, itemId, kind, delta) {
15218
15299
  const block = this.ensureStreamBlock(follow, key, itemId, kind);
15219
15300
  block.text = appendBoundedText(block.text, delta);
15220
- this.pushEvent(follow, "assistant/chunk", {
15221
- turn: follow.turn,
15222
- step: 1,
15223
- 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
15224
15305
  });
15225
15306
  }
15226
15307
  closeItemStreamBlocks(follow, itemId, fallback) {
15227
15308
  const matches = [...follow.streamedBlocks].filter(([, block]) => block.itemId === itemId);
15228
15309
  for (const [key, block] of matches) {
15229
15310
  const text = block.text || fallback || "";
15230
- this.pushEvent(follow, "assistant/chunk", {
15231
- turn: follow.turn,
15232
- step: 1,
15233
- 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 }
15234
15315
  });
15235
15316
  follow.streamedBlocks.delete(key);
15236
15317
  }
15237
15318
  }
15238
15319
  closeAllStreamBlocks(follow) {
15239
15320
  for (const block of follow.streamedBlocks.values()) {
15240
- this.pushEvent(follow, "assistant/chunk", {
15241
- turn: follow.turn,
15242
- step: 1,
15243
- 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 }
15244
15325
  });
15245
15326
  }
15246
15327
  follow.streamedBlocks.clear();
15247
15328
  }
15248
15329
  finishStream(follow) {
15249
- this.pushEvent(follow, "assistant/chunk", {
15250
- turn: follow.turn,
15251
- step: 1,
15252
- chunk: { type: "finish", reason: { kind: "stop" } }
15253
- });
15330
+ this.pushAssistantChunk(follow, { type: "finish", reason: { kind: "stop" } });
15254
15331
  follow.streamActive = false;
15255
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
+ }
15256
15394
  ensureToolStarted(follow, item) {
15257
15395
  const itemId = string(item.id);
15258
15396
  if (itemId === void 0) return;
@@ -15295,6 +15433,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15295
15433
  follow.streamedBlocks.clear();
15296
15434
  follow.nextBlockIndex = 0;
15297
15435
  follow.streamActive = false;
15436
+ follow.assistantAttempt = void 0;
15298
15437
  follow.liveItems.clear();
15299
15438
  follow.liveToolOutput.clear();
15300
15439
  follow.liveToolResultSeq.clear();
@@ -15307,7 +15446,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15307
15446
  this.broadcastRcHost({ type: "host/session-status", sessionId, running: args[1] });
15308
15447
  } else if (event === "api-session/removed" && sessionId !== void 0) {
15309
15448
  this.broadcastRcHost({ type: "host/session-removed", sessionId });
15310
- } else if (event === "api-session/added" && isRecord3(args[0])) {
15449
+ } else if (event === "api-session/added" && isRecord4(args[0])) {
15311
15450
  const summary = args[0];
15312
15451
  this.broadcastRcHost({
15313
15452
  type: "host/session-added",
@@ -15565,7 +15704,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15565
15704
  const thread = record2(result.thread);
15566
15705
  if (string(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
15567
15706
  value = paginateCodexNativeHistory(
15568
- projectCodexNativeHistory(thread, `codex:${threadId}`),
15707
+ projectCodexNativeHistory(thread, `codex:${threadId}`, this.sessionGeneration),
15569
15708
  {
15570
15709
  beforeSeq: page.beforeSeq,
15571
15710
  throughSeq: page.throughSeq,
@@ -15573,6 +15712,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15573
15712
  }
15574
15713
  );
15575
15714
  }
15715
+ value = adaptCodexHistoryPage(value, this.sessionGeneration);
15576
15716
  if (!isCodexNativeHistoryPage(value, `codex:${threadId}`)) {
15577
15717
  throw new Error("CodeX Remote returned an invalid paginated History.");
15578
15718
  }
@@ -15640,6 +15780,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15640
15780
  streamedBlocks: /* @__PURE__ */ new Map(),
15641
15781
  nextBlockIndex: 0,
15642
15782
  streamActive: false,
15783
+ assistantStreamRevision: 0,
15643
15784
  liveItems: /* @__PURE__ */ new Map(),
15644
15785
  liveToolOutput: /* @__PURE__ */ new Map(),
15645
15786
  liveToolResultSeq: /* @__PURE__ */ new Map(),
@@ -15690,7 +15831,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15690
15831
  }
15691
15832
  createApiProxy() {
15692
15833
  const call = (endpoint) => async (request, signal) => {
15693
- 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;
15694
15835
  const result = await this.dispatch(rcEndpoint(endpoint), { args: { request: payload } }, signal ?? new AbortController().signal);
15695
15836
  return {
15696
15837
  rpcId: request.rpcId,
@@ -15922,7 +16063,7 @@ async function loadModelDirectory(client, signal) {
15922
16063
  models
15923
16064
  };
15924
16065
  }
15925
- function projectCodexNativeHistory(thread, sessionId) {
16066
+ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legacy") {
15926
16067
  const entries = [];
15927
16068
  let seq = 0;
15928
16069
  let turnNumber = 0;
@@ -15935,7 +16076,7 @@ function projectCodexNativeHistory(thread, sessionId) {
15935
16076
  type,
15936
16077
  seq: eventSeq,
15937
16078
  time,
15938
- data,
16079
+ data: adaptEventData(type, data, sessionGeneration),
15939
16080
  ...sourceEventSeqs === void 0 ? {} : { sourceEventSeqs },
15940
16081
  ...isSurfaceEvent(type) ? { surfaceOp: "append" } : {}
15941
16082
  },
@@ -15988,10 +16129,11 @@ function projectCodexNativeHistory(thread, sessionId) {
15988
16129
  const projected = projectCodexThread(thread);
15989
16130
  return {
15990
16131
  header: {
15991
- version: 1,
16132
+ version: sessionGeneration === "v3" ? 3 : 1,
15992
16133
  id: sessionId,
15993
16134
  createdAt: projected?.createdAt ?? Date.now(),
15994
- ...projected?.cwd === void 0 ? {} : { cwd: projected.cwd }
16135
+ ...projected?.cwd === void 0 ? {} : { cwd: projected.cwd },
16136
+ ...sessionGeneration === "v3" ? { isSeeded: false } : {}
15995
16137
  },
15996
16138
  entries,
15997
16139
  lastSeq: seq - 1,
@@ -16024,6 +16166,40 @@ function paginateCodexNativeHistory(history, request) {
16024
16166
  ...history.activeTurnId === void 0 ? {} : { activeTurnId: history.activeTurnId }
16025
16167
  };
16026
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
+ }
16027
16203
  function itemEvents(item, turn, step, requestId, selection = modelSelection(), sessionId = CODEX_SESSION_PREFIX, supplementalImages = []) {
16028
16204
  const type = string(item.type);
16029
16205
  const id2 = string(item.id) ?? `${turn}:${step}:${hashString(JSON.stringify(item))}`;
@@ -16180,7 +16356,7 @@ function collectImageBlocks(value, output = []) {
16180
16356
  for (const item of value) collectImageBlocks(item, output);
16181
16357
  return output;
16182
16358
  }
16183
- if (!isRecord3(value)) return output;
16359
+ if (!isRecord4(value)) return output;
16184
16360
  if (value.type === "image") output.push(value);
16185
16361
  for (const [key, child] of Object.entries(value)) {
16186
16362
  if (key === "attachment") continue;
@@ -16510,7 +16686,7 @@ function codexPromptInput(content) {
16510
16686
  if (content.length === 0 || content.length > MAX_CODEX_PROMPT_PARTS) return void 0;
16511
16687
  const input2 = [];
16512
16688
  for (const value of content) {
16513
- if (!isRecord3(value)) return void 0;
16689
+ if (!isRecord4(value)) return void 0;
16514
16690
  if (value.type === "text") {
16515
16691
  if (typeof value.text !== "string" || value.text.length === 0 || value.text.length > MAX_CODEX_PROMPT_TEXT) return void 0;
16516
16692
  input2.push({ type: "text", text: value.text });
@@ -16763,12 +16939,12 @@ function errorCode(error) {
16763
16939
  return "code" in error && typeof error.code === "string" ? error.code : void 0;
16764
16940
  }
16765
16941
  function errorDetails(error) {
16766
- return "details" in error && isRecord3(error.details) ? error.details : {};
16942
+ return "details" in error && isRecord4(error.details) ? error.details : {};
16767
16943
  }
16768
16944
  function record2(value) {
16769
- return isRecord3(value) ? value : {};
16945
+ return isRecord4(value) ? value : {};
16770
16946
  }
16771
- function isRecord3(value) {
16947
+ function isRecord4(value) {
16772
16948
  return typeof value === "object" && value !== null && !Array.isArray(value);
16773
16949
  }
16774
16950
  function array(value) {
@@ -16982,7 +17158,7 @@ function normalizeServerUrl(value) {
16982
17158
  }
16983
17159
 
16984
17160
  // src/version.ts
16985
- var PLUGIN_VERSION = "0.4.10";
17161
+ var PLUGIN_VERSION = "0.4.12";
16986
17162
 
16987
17163
  // src/server-api.ts
16988
17164
  var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
@@ -17501,7 +17677,7 @@ var TypertGatewaySwitch = class {
17501
17677
  if (normalized !== void 0) return normalized;
17502
17678
  const source = error instanceof Error ? error : new Error("The Harness Gateway rejected the request.");
17503
17679
  const code = "code" in source && typeof source.code === "string" ? source.code : "internal";
17504
- const details = "details" in source && isRecord4(source.details) ? source.details : {};
17680
+ const details = "details" in source && isRecord5(source.details) ? source.details : {};
17505
17681
  return { code, message: source.message, details };
17506
17682
  }
17507
17683
  };
@@ -17510,7 +17686,7 @@ function requestFromCarrier(endpoint, payload, signal) {
17510
17686
  if (segments.length !== 2 || segments.some((segment) => segment.length === 0)) {
17511
17687
  throw new Error("The Harness Gateway endpoint is invalid.");
17512
17688
  }
17513
- if (!isRecord4(payload) || !isRecord4(payload.args)) {
17689
+ if (!isRecord5(payload) || !isRecord5(payload.args)) {
17514
17690
  throw new Error("The Harness Gateway payload is invalid.");
17515
17691
  }
17516
17692
  return { namespace: segments[0], method: segments[1], args: payload.args, signal };
@@ -17525,7 +17701,7 @@ function isLocalOnlyEndpoint(endpoint) {
17525
17701
  function isRemoteCommandMethod(method) {
17526
17702
  return REMOTE_COMMAND_METHODS.includes(method);
17527
17703
  }
17528
- function isRecord4(value) {
17704
+ function isRecord5(value) {
17529
17705
  return typeof value === "object" && value !== null && !Array.isArray(value);
17530
17706
  }
17531
17707
 
@@ -17536,7 +17712,7 @@ import { networkInterfaces } from "node:os";
17536
17712
  // src/native-rtc-helper.ts
17537
17713
  import { spawn, spawnSync } from "node:child_process";
17538
17714
  import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
17539
- import { delimiter, dirname, join } from "node:path";
17715
+ import { delimiter, dirname as dirname2, join as join2 } from "node:path";
17540
17716
  import { fileURLToPath } from "node:url";
17541
17717
  var cachedExternalFactory;
17542
17718
  var cachedExternalFactoryResolved = false;
@@ -17587,21 +17763,21 @@ function nodeBinaryCandidates() {
17587
17763
  add3(process.env.DSH_REMOTE_NODE);
17588
17764
  add3(process.env.NODE);
17589
17765
  add3(process.execPath);
17590
- 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"));
17591
17767
  add3("/opt/homebrew/bin/node");
17592
17768
  add3("/usr/local/bin/node");
17593
17769
  add3("/usr/bin/node");
17594
- add3(join(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
17595
- add3(join(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
17596
- 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"));
17597
17773
  for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
17598
17774
  return candidates;
17599
17775
  }
17600
17776
  function nvmNodeCandidates() {
17601
- const root = join(process.env.HOME ?? "", ".nvm", "versions", "node");
17777
+ const root = join2(process.env.HOME ?? "", ".nvm", "versions", "node");
17602
17778
  if (root === "" || !existsSync(root)) return [];
17603
17779
  try {
17604
- 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 }));
17605
17781
  } catch {
17606
17782
  return [];
17607
17783
  }
@@ -17623,7 +17799,7 @@ function isUsableExternalNode(candidate, requireFrom) {
17623
17799
  "console.log(`node:${process.versions.node}`);"
17624
17800
  ].join("")
17625
17801
  ], {
17626
- cwd: dirname(requireFrom),
17802
+ cwd: dirname2(requireFrom),
17627
17803
  env,
17628
17804
  encoding: "utf8",
17629
17805
  timeout: 3e3
@@ -17645,7 +17821,7 @@ var ExternalNativePeerConnection = class {
17645
17821
  this.nodeBinary = nodeBinary;
17646
17822
  this.requireFrom = requireFrom;
17647
17823
  this.child = spawn(this.nodeBinary, ["--input-type=module", "--eval", HELPER_SOURCE], {
17648
- cwd: dirname(this.requireFrom),
17824
+ cwd: dirname2(this.requireFrom),
17649
17825
  env: { ...process.env, DSH_REMOTE_RTC_HELPER_REQUIRE_FROM: this.requireFrom },
17650
17826
  stdio: ["pipe", "pipe", "pipe"]
17651
17827
  });
@@ -18855,7 +19031,7 @@ var ClientModeRuntime = class {
18855
19031
  const virtual = CodexVirtualHarness.remote(remote.client, {
18856
19032
  deviceId: remote.target.deviceId,
18857
19033
  name: remote.target.name
18858
- });
19034
+ }, harnessSessionGeneration(this.host?.localHarnessVersion?.()));
18859
19035
  let workspace;
18860
19036
  try {
18861
19037
  workspace = await virtual.selectWorkspace(workspaceId, signal);
@@ -18972,10 +19148,10 @@ var ClientModeRuntime = class {
18972
19148
  }
18973
19149
  }
18974
19150
  stream.unsubscribe = remote.client.onEvent((event) => {
18975
- 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) {
18976
19152
  this.appendCodexFrame(stream, event.data);
18977
19153
  }
18978
- 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) {
18979
19155
  stream.closed = typeof event.data.reason === "string" ? event.data.reason : "closed";
18980
19156
  stream.wake();
18981
19157
  }
@@ -19002,7 +19178,7 @@ var ClientModeRuntime = class {
19002
19178
  stream.wake();
19003
19179
  };
19004
19180
  appendCodexFrame(stream, data) {
19005
- 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;
19006
19182
  if (stream.frames.length >= 256) {
19007
19183
  stream.closed = "overflow";
19008
19184
  } else {
@@ -19087,7 +19263,16 @@ var ClientModeRuntime = class {
19087
19263
  }
19088
19264
  assertRemoteCompatible(remote) {
19089
19265
  const localRemoteGateway = this.gatewaySwitch.supportsCarrier();
19090
- 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;
19091
19276
  throw new ClientModeError(
19092
19277
  "HARNESS_VERSION_INCOMPATIBLE",
19093
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."
@@ -19216,7 +19401,8 @@ var ClientModeRuntime = class {
19216
19401
  transport: connectedTransport,
19217
19402
  features,
19218
19403
  progressRunId,
19219
- ...serverDevice.clientVersion === void 0 ? {} : { clientVersion: serverDevice.clientVersion }
19404
+ ...serverDevice.clientVersion === void 0 ? {} : { clientVersion: serverDevice.clientVersion },
19405
+ ...serverDevice.harnessVersion === void 0 ? {} : { harnessVersion: serverDevice.harnessVersion }
19220
19406
  };
19221
19407
  } catch (error) {
19222
19408
  this.clearConnectionProgress(progressRunId);
@@ -19468,7 +19654,7 @@ function record3(value) {
19468
19654
  }
19469
19655
  return value;
19470
19656
  }
19471
- function isRecord5(value) {
19657
+ function isRecord6(value) {
19472
19658
  return typeof value === "object" && value !== null && !Array.isArray(value);
19473
19659
  }
19474
19660
  function ok2(value) {
@@ -19493,11 +19679,11 @@ async function readRemoteWorkspaceBaseline(gateway, signal) {
19493
19679
  const iterator = source[Symbol.asyncIterator]();
19494
19680
  try {
19495
19681
  const first = await iterator.next();
19496
- 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)) {
19497
19683
  throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace baseline.");
19498
19684
  }
19499
19685
  return first.value.value.items.map((item) => {
19500
- 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") {
19501
19687
  throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace row.");
19502
19688
  }
19503
19689
  return { workspaceId: item.workspaceId, path: item.path, title: item.title };
@@ -19562,12 +19748,18 @@ async function probeRemoteHostFeatures(client, clientVersion) {
19562
19748
  if (error instanceof Error && "code" in error && error.code === "METHOD_NOT_FOUND") return fallback;
19563
19749
  throw error;
19564
19750
  }
19565
- 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")) {
19566
19752
  throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned invalid transport capabilities.");
19567
19753
  }
19568
19754
  const capabilities = new Set(value.capabilities);
19569
19755
  const apiProxy = capabilities.has("harness.api.v1");
19570
- 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;
19571
19763
  if (!apiProxy && !remoteGateway) {
19572
19764
  throw new ClientModeError("FEATURE_NOT_SUPPORTED", "The remote Host exposes no supported Harness transport.");
19573
19765
  }
@@ -19576,6 +19768,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
19576
19768
  fileViewer: capabilities.has("fileviewer.read.v1"),
19577
19769
  apiProxy,
19578
19770
  remoteGateway,
19771
+ ...sessionFormat === void 0 ? {} : { sessionFormat },
19579
19772
  codex: capabilities.has("codex.appserver.v1")
19580
19773
  };
19581
19774
  }
@@ -19642,9 +19835,9 @@ import { hostname as hostname2 } from "node:os";
19642
19835
 
19643
19836
  // src/identity-store.ts
19644
19837
  import { createHash } from "node:crypto";
19645
- 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";
19646
19839
  import { homedir } from "node:os";
19647
- import { dirname as dirname2, join as join2 } from "node:path";
19840
+ import { dirname as dirname3, join as join3 } from "node:path";
19648
19841
  var identitySchema = external_exports.object({
19649
19842
  schemaVersion: external_exports.literal(1),
19650
19843
  deviceId: external_exports.string().uuid(),
@@ -19673,14 +19866,14 @@ var IdentityStore = class {
19673
19866
  peers = /* @__PURE__ */ new Map();
19674
19867
  constructor(options = {}) {
19675
19868
  const env = options.env ?? process.env;
19676
- const dshHome = env.DSH_HOME || join2(options.homeDirectory ?? homedir(), ".dsh");
19677
- 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");
19678
19871
  }
19679
19872
  async loadOrCreate(deviceName) {
19680
19873
  await mkdir(this.directory, { recursive: true, mode: 448 });
19681
19874
  await chmod(this.directory, 448);
19682
- const devicePath = join2(this.directory, "device.json");
19683
- const keyPath = join2(this.directory, "device.key");
19875
+ const devicePath = join3(this.directory, "device.json");
19876
+ const keyPath = join3(this.directory, "device.key");
19684
19877
  const [hasDevice, hasKey] = await Promise.all([exists(devicePath), exists(keyPath)]);
19685
19878
  if (hasDevice !== hasKey) {
19686
19879
  throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
@@ -19694,8 +19887,8 @@ var IdentityStore = class {
19694
19887
  }
19695
19888
  await assertPrivateMode(keyPath);
19696
19889
  try {
19697
- let record6 = identitySchema.parse(JSON.parse(await readFile(devicePath, "utf8")));
19698
- 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();
19699
19892
  const regenerated = generateKeyPair(fromBase64Url2(privateKey));
19700
19893
  if (regenerated.publicKey !== record6.publicKey) {
19701
19894
  throw new IdentityInvalidError("device public and private keys do not match");
@@ -19749,11 +19942,11 @@ var IdentityStore = class {
19749
19942
  return removed;
19750
19943
  }
19751
19944
  async loadPeers() {
19752
- const path = join2(this.directory, "trusted-peers.json");
19945
+ const path = join3(this.directory, "trusted-peers.json");
19753
19946
  if (!await exists(path)) {
19754
19947
  await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
19755
19948
  }
19756
- const parsed = trustedPeersSchema.parse(JSON.parse(await readFile(path, "utf8")));
19949
+ const parsed = trustedPeersSchema.parse(JSON.parse(await readFile2(path, "utf8")));
19757
19950
  const peers = /* @__PURE__ */ new Map();
19758
19951
  for (const peer of parsed.peers) {
19759
19952
  if (peer.fingerprint !== fingerprint(peer.publicKey)) {
@@ -19765,7 +19958,7 @@ var IdentityStore = class {
19765
19958
  this.peers = peers;
19766
19959
  }
19767
19960
  async savePeers() {
19768
- await atomicJsonWrite(join2(this.directory, "trusted-peers.json"), {
19961
+ await atomicJsonWrite(join3(this.directory, "trusted-peers.json"), {
19769
19962
  schemaVersion: 1,
19770
19963
  peers: [...this.peers.values()]
19771
19964
  }, 384);
@@ -19774,7 +19967,7 @@ var IdentityStore = class {
19774
19967
  function serverStorageDirectory(root, serverUrl, role) {
19775
19968
  const origin = new URL(serverUrl).origin;
19776
19969
  const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
19777
- return join2(root, "servers", scope, role);
19970
+ return join3(root, "servers", scope, role);
19778
19971
  }
19779
19972
  function fingerprint(publicKey) {
19780
19973
  const compact = createHash("sha256").update(fromBase64Url2(publicKey)).digest("hex").slice(0, 12).toUpperCase();
@@ -19792,7 +19985,7 @@ async function atomicJsonWrite(path, value, mode) {
19792
19985
  `, mode);
19793
19986
  }
19794
19987
  async function atomicTextWrite(path, value, mode) {
19795
- await mkdir(dirname2(path), { recursive: true, mode: 448 });
19988
+ await mkdir(dirname3(path), { recursive: true, mode: 448 });
19796
19989
  const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
19797
19990
  await writeFile(temporary, value, { encoding: "utf8", mode, flag: "wx" });
19798
19991
  await chmod(temporary, mode);
@@ -19816,8 +20009,8 @@ function safeErrorMessage(error) {
19816
20009
  }
19817
20010
 
19818
20011
  // src/server-credentials.ts
19819
- 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";
19820
- 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";
19821
20014
  var credentialSchema = external_exports.object({
19822
20015
  schemaVersion: external_exports.literal(1),
19823
20016
  serverUrl: external_exports.string().url(),
@@ -19832,14 +20025,14 @@ var credentialSchema = external_exports.object({
19832
20025
  var ServerCredentialStore = class {
19833
20026
  path;
19834
20027
  constructor(directory) {
19835
- this.path = join3(directory, "server-credentials.json");
20028
+ this.path = join4(directory, "server-credentials.json");
19836
20029
  }
19837
20030
  async load(serverUrl, deviceId) {
19838
20031
  if (!await exists2(this.path)) return void 0;
19839
20032
  await assertPrivateMode2(this.path);
19840
20033
  let parsed;
19841
20034
  try {
19842
- parsed = credentialSchema.parse(JSON.parse(await readFile2(this.path, "utf8")));
20035
+ parsed = credentialSchema.parse(JSON.parse(await readFile3(this.path, "utf8")));
19843
20036
  } catch (error) {
19844
20037
  throw new ServerCredentialsInvalidError(`server credentials are invalid: ${safeMessage(error)}`);
19845
20038
  }
@@ -19859,7 +20052,7 @@ var ServerCredentialsInvalidError = class extends Error {
19859
20052
  code = "SERVER_CREDENTIALS_INVALID";
19860
20053
  };
19861
20054
  async function atomicWrite(path, contents) {
19862
- await mkdir2(dirname3(path), { recursive: true, mode: 448 });
20055
+ await mkdir2(dirname4(path), { recursive: true, mode: 448 });
19863
20056
  const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
19864
20057
  await writeFile2(temporary, contents, { encoding: "utf8", mode: 384, flag: "wx" });
19865
20058
  await chmod2(temporary, 384);
@@ -20110,11 +20303,11 @@ function editableConfig(config) {
20110
20303
  }
20111
20304
  };
20112
20305
  }
20113
- function isRecord6(value) {
20306
+ function isRecord7(value) {
20114
20307
  return typeof value === "object" && value !== null && !Array.isArray(value);
20115
20308
  }
20116
20309
  function record4(value) {
20117
- if (!isRecord6(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
20310
+ if (!isRecord7(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
20118
20311
  return value;
20119
20312
  }
20120
20313
  function ok3(value) {
@@ -20337,36 +20530,6 @@ function shortId2(value) {
20337
20530
  return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
20338
20531
  }
20339
20532
 
20340
- // src/harness-version.ts
20341
- import { readFile as readFile3 } from "node:fs/promises";
20342
- import { dirname as dirname4, isAbsolute, join as join4 } from "node:path";
20343
- var LEGACY_PLACEHOLDER_VERSION = "0.0.1";
20344
- function normalizeHarnessVersion(value) {
20345
- if (typeof value !== "string") return void 0;
20346
- const version = value.trim();
20347
- if (version.length === 0 || version.length > 64 || /[\u0000-\u001f]/u.test(version)) return void 0;
20348
- return version;
20349
- }
20350
- function selectHarnessVersion(reportedVersion, distributionVersion) {
20351
- if (reportedVersion !== void 0 && reportedVersion !== LEGACY_PLACEHOLDER_VERSION) return reportedVersion;
20352
- return distributionVersion;
20353
- }
20354
- async function readHarnessDistributionVersion(entrypoint = process.argv[1]) {
20355
- if (entrypoint === void 0 || !isAbsolute(entrypoint)) return void 0;
20356
- let directory = dirname4(entrypoint);
20357
- for (let depth = 0; depth < 8; depth += 1) {
20358
- try {
20359
- const manifest = JSON.parse(await readFile3(join4(directory, "package.json"), "utf8"));
20360
- if (manifest.name === "@deepseek-ai/dsh") return normalizeHarnessVersion(manifest.version);
20361
- } catch {
20362
- }
20363
- const parent = dirname4(directory);
20364
- if (parent === directory) break;
20365
- directory = parent;
20366
- }
20367
- return void 0;
20368
- }
20369
-
20370
20533
  // src/safe-error.ts
20371
20534
  var RpcError = class extends Error {
20372
20535
  constructor(code, message, details, retryable = false) {
@@ -22721,13 +22884,13 @@ function needsDirectoryFallback(result) {
22721
22884
  }
22722
22885
  function requestArgs(payload) {
22723
22886
  const root = record5(payload);
22724
- const args = isRecord7(root.args) ? root.args : root;
22887
+ const args = isRecord8(root.args) ? root.args : root;
22725
22888
  return record5(args.request ?? args._request ?? args);
22726
22889
  }
22727
22890
  function record5(value) {
22728
- return isRecord7(value) ? value : {};
22891
+ return isRecord8(value) ? value : {};
22729
22892
  }
22730
- function isRecord7(value) {
22893
+ function isRecord8(value) {
22731
22894
  return typeof value === "object" && value !== null && !Array.isArray(value);
22732
22895
  }
22733
22896
 
@@ -22921,7 +23084,7 @@ var CodexAppServerClient = class {
22921
23084
  this.handleProcessFailure("CODEX_INVALID_RESPONSE", new Error("Codex App Server emitted invalid JSON."));
22922
23085
  return;
22923
23086
  }
22924
- if (!isRecord8(value)) {
23087
+ if (!isRecord9(value)) {
22925
23088
  this.handleProcessFailure("CODEX_INVALID_RESPONSE", new Error("Codex App Server emitted an invalid message."));
22926
23089
  return;
22927
23090
  }
@@ -22966,12 +23129,12 @@ var CodexAppServerClient = class {
22966
23129
  }
22967
23130
  };
22968
23131
  function safeUpstreamError(value) {
22969
- 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.";
22970
23133
  const message = value.message.toLowerCase();
22971
23134
  if (message.includes("active writer")) return "Codex thread already has an active writer.";
22972
23135
  return message.includes("not initialized") ? "Codex App Server is not initialized." : "Codex App Server rejected the request.";
22973
23136
  }
22974
- function isRecord8(value) {
23137
+ function isRecord9(value) {
22975
23138
  return typeof value === "object" && value !== null && !Array.isArray(value);
22976
23139
  }
22977
23140
 
@@ -23341,15 +23504,15 @@ function decodeCanonicalBase643(value) {
23341
23504
  }
23342
23505
  return decoded;
23343
23506
  }
23344
- function isRecord9(value) {
23507
+ function isRecord10(value) {
23345
23508
  return typeof value === "object" && value !== null && !Array.isArray(value);
23346
23509
  }
23347
23510
  function safeErrorCode2(error) {
23348
- if (isRecord9(error) && typeof error.code === "string") return error.code;
23511
+ if (isRecord10(error) && typeof error.code === "string") return error.code;
23349
23512
  return "UNKNOWN";
23350
23513
  }
23351
23514
  function safeMethod(input2) {
23352
- return isRecord9(input2) && typeof input2.method === "string" ? input2.method : "invalid";
23515
+ return isRecord10(input2) && typeof input2.method === "string" ? input2.method : "invalid";
23353
23516
  }
23354
23517
  function concatChunks3(chunks, totalBytes) {
23355
23518
  const output = new Uint8Array(totalBytes);
@@ -23766,14 +23929,14 @@ var CodexRemoteDomain = class {
23766
23929
  await this.assertResultThreadAllowed(result);
23767
23930
  if (codexPermissionPresetFromResponse(result) !== params.permissionPreset) {
23768
23931
  await this.callUpstream("thread/settings/update", { threadId, ...settings });
23769
- result = { ...isRecord10(result) ? result : {}, ...settings, sandbox: settings.sandboxPolicy };
23932
+ result = { ...isRecord11(result) ? result : {}, ...settings, sandbox: settings.sandboxPolicy };
23770
23933
  }
23771
23934
  }
23772
23935
  await this.publishPermission(threadId, result);
23773
23936
  return result;
23774
23937
  }
23775
23938
  async publishPermission(threadId, value) {
23776
- const source = isRecord10(value) ? value : {};
23939
+ const source = isRecord11(value) ? value : {};
23777
23940
  const threadSettings = { approvalPolicy: source.approvalPolicy, sandboxPolicy: source.sandboxPolicy ?? source.sandbox };
23778
23941
  await this.handleInbound({ kind: "notification", method: "thread/settings/updated", params: { threadId, threadSettings } });
23779
23942
  }
@@ -23869,9 +24032,9 @@ var CodexRemoteDomain = class {
23869
24032
  itemsView,
23870
24033
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23871
24034
  });
23872
- const pageResult = isRecord10(result) ? result : {};
24035
+ const pageResult = isRecord11(result) ? result : {};
23873
24036
  for (const rawTurn of array2(pageResult.data)) {
23874
- if (!isRecord10(rawTurn)) continue;
24037
+ if (!isRecord11(rawTurn)) continue;
23875
24038
  const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
23876
24039
  const items = rawTurn.itemsView === "full" || turnId === void 0 ? array2(rawTurn.items) : await this.readThreadItems(connectionId, threadId, turnId, array2(rawTurn.items));
23877
24040
  turns.push({ ...rawTurn, items });
@@ -23893,9 +24056,9 @@ var CodexRemoteDomain = class {
23893
24056
  sortDirection: "asc",
23894
24057
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23895
24058
  });
23896
- const pageResult = isRecord10(result) ? result : {};
24059
+ const pageResult = isRecord11(result) ? result : {};
23897
24060
  for (const entry of array2(pageResult.data)) {
23898
- if (isRecord10(entry) && entry.item !== void 0) items.push(entry.item);
24061
+ if (isRecord11(entry) && entry.item !== void 0) items.push(entry.item);
23899
24062
  }
23900
24063
  cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
23901
24064
  if (cursor2 === void 0) break;
@@ -23959,7 +24122,7 @@ var CodexRemoteDomain = class {
23959
24122
  return [...this.peers.values()].some((peer) => peer.hasThreadSubscription(threadId));
23960
24123
  }
23961
24124
  resolveUpstreamApproval(params) {
23962
- 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;
23963
24126
  for (const [handle, approval] of this.approvals) {
23964
24127
  if (approval.upstreamId === params.requestId) this.approvals.delete(handle);
23965
24128
  }
@@ -24176,24 +24339,24 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
24176
24339
  ])];
24177
24340
  }
24178
24341
  function parseCallEnvelope(input2) {
24179
- 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")) {
24180
24343
  throw new RpcError("INVALID_MESSAGE", "The Codex call envelope is invalid.");
24181
24344
  }
24182
24345
  return { method: input2.method, params: input2.params };
24183
24346
  }
24184
24347
  function parseRespond(input2) {
24185
- 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")) {
24186
24349
  throw new RpcError("INVALID_MESSAGE", "The Codex approval response is invalid.");
24187
24350
  }
24188
24351
  return input2;
24189
24352
  }
24190
24353
  function accountCanRun(result) {
24191
- if (!isRecord10(result) || typeof result.requiresOpenaiAuth !== "boolean") return false;
24192
- 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);
24193
24356
  }
24194
24357
  function sanitizeAccount(result) {
24195
- if (!isRecord10(result)) throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned invalid account state.");
24196
- 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;
24197
24360
  return {
24198
24361
  authenticated: account !== void 0 || result.requiresOpenaiAuth === false,
24199
24362
  requiresOpenaiAuth: result.requiresOpenaiAuth === true,
@@ -24206,11 +24369,11 @@ function sanitizeAccount(result) {
24206
24369
  };
24207
24370
  }
24208
24371
  function sanitizeThreadList(result) {
24209
- if (!isRecord10(result) || !Array.isArray(result.data)) {
24372
+ if (!isRecord11(result) || !Array.isArray(result.data)) {
24210
24373
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
24211
24374
  }
24212
24375
  const data = result.data.flatMap((value) => {
24213
- if (!isRecord10(value) || typeof value.id !== "string") return [];
24376
+ if (!isRecord11(value) || typeof value.id !== "string") return [];
24214
24377
  return [{
24215
24378
  id: value.id,
24216
24379
  ...typeof value.sessionId === "string" ? { sessionId: value.sessionId } : {},
@@ -24222,7 +24385,7 @@ function sanitizeThreadList(result) {
24222
24385
  ...typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) ? { updatedAt: value.updatedAt } : {},
24223
24386
  ...typeof value.archived === "boolean" ? { archived: value.archived } : {},
24224
24387
  ...typeof value.isPinned === "boolean" ? { isPinned: value.isPinned } : {},
24225
- ...isRecord10(value.status) ? { status: value.status } : {}
24388
+ ...isRecord11(value.status) ? { status: value.status } : {}
24226
24389
  }];
24227
24390
  });
24228
24391
  return {
@@ -24232,18 +24395,18 @@ function sanitizeThreadList(result) {
24232
24395
  };
24233
24396
  }
24234
24397
  function filterThreadListByWorkspaceAuthority(result, authority) {
24235
- if (!isRecord10(result) || !Array.isArray(result.data)) {
24398
+ if (!isRecord11(result) || !Array.isArray(result.data)) {
24236
24399
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
24237
24400
  }
24238
24401
  return {
24239
24402
  ...result,
24240
- data: result.data.map((record6) => isRecord10(record6) ? record6 : 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))
24241
24404
  };
24242
24405
  }
24243
24406
  function sanitizeProject(value) {
24244
- 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;
24245
24408
  const roots = Array.isArray(value.roots) ? value.roots.flatMap((root) => {
24246
- 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;
24247
24410
  return path === void 0 || !isAbsolute3(path) ? [] : [{ path }];
24248
24411
  }) : [];
24249
24412
  if (roots.length === 0) return void 0;
@@ -24257,7 +24420,7 @@ function sanitizeProject(value) {
24257
24420
  };
24258
24421
  }
24259
24422
  function sanitizeProjectList(result) {
24260
- if (!isRecord10(result) || !Array.isArray(result.data)) {
24423
+ if (!isRecord11(result) || !Array.isArray(result.data)) {
24261
24424
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid project list.");
24262
24425
  }
24263
24426
  const data = result.data.flatMap((value) => sanitizeProject(value) ?? []);
@@ -24267,7 +24430,7 @@ function sanitizeProjectList(result) {
24267
24430
  };
24268
24431
  }
24269
24432
  function sanitizeProjectCreate(result) {
24270
- const project = isRecord10(result) ? sanitizeProject(result.project) : void 0;
24433
+ const project = isRecord11(result) ? sanitizeProject(result.project) : void 0;
24271
24434
  if (project === void 0) {
24272
24435
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid created project.");
24273
24436
  }
@@ -24299,19 +24462,19 @@ function codexDirectoryCrumbs(root, path) {
24299
24462
  return crumbs2;
24300
24463
  }
24301
24464
  function extractThread(result) {
24302
- return isRecord10(result) && isRecord10(result.thread) ? result.thread : void 0;
24465
+ return isRecord11(result) && isRecord11(result.thread) ? result.thread : void 0;
24303
24466
  }
24304
24467
  function extractTurnId(result) {
24305
- if (!isRecord10(result)) return void 0;
24468
+ if (!isRecord11(result)) return void 0;
24306
24469
  if (typeof result.turnId === "string" && result.turnId.length > 0) return result.turnId;
24307
- 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;
24308
24471
  return void 0;
24309
24472
  }
24310
24473
  function extractThreadId(params) {
24311
- if (!isRecord10(params)) return void 0;
24474
+ if (!isRecord11(params)) return void 0;
24312
24475
  if (typeof params.threadId === "string") return params.threadId;
24313
- if (isRecord10(params.thread) && typeof params.thread.id === "string") return params.thread.id;
24314
- 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;
24315
24478
  return void 0;
24316
24479
  }
24317
24480
  function optionalInteger2(value) {
@@ -24353,13 +24516,13 @@ function mapCodexImageInputs(params) {
24353
24516
  return {
24354
24517
  ...params,
24355
24518
  input: params.input.map((value) => {
24356
- 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;
24357
24520
  return { type: "image", url: `data:${value.mediaType};base64,${value.data}` };
24358
24521
  })
24359
24522
  };
24360
24523
  }
24361
24524
  function sanitizeApprovalParams(params, requestHandle) {
24362
- if (!isRecord10(params)) return { requestHandle };
24525
+ if (!isRecord11(params)) return { requestHandle };
24363
24526
  const safe = { ...params };
24364
24527
  delete safe.proposedExecpolicyAmendment;
24365
24528
  delete safe.additionalPermissions;
@@ -24390,7 +24553,7 @@ function isProjectListFallbackError(error) {
24390
24553
  function canTryNextBinary(error) {
24391
24554
  return !(error instanceof RpcError) || !["CODEX_AUTH_REQUIRED", "CODEX_CLOSED"].includes(error.code);
24392
24555
  }
24393
- function isRecord10(value) {
24556
+ function isRecord11(value) {
24394
24557
  return typeof value === "object" && value !== null && !Array.isArray(value);
24395
24558
  }
24396
24559
  function array2(value) {
@@ -24498,6 +24661,9 @@ var HostPluginRuntime = class {
24498
24661
  accountRequired: error === "ACCOUNT_AUTH_REQUIRED" || error === "AUTH_INVALID" || error === "TOKEN_EXPIRED"
24499
24662
  };
24500
24663
  }
24664
+ localHarnessVersion() {
24665
+ return this.harnessVersion;
24666
+ }
24501
24667
  reconnectHost() {
24502
24668
  if (this.closed) throw new Error("remote runtime is closed");
24503
24669
  if (this.serverConnection === void 0) {
@@ -24667,9 +24833,13 @@ var HostPluginRuntime = class {
24667
24833
  }
24668
24834
  hostCapabilities() {
24669
24835
  const capabilities = [];
24670
- if (this.apiProxy !== void 0) capabilities.push("harness.api.v1", "harness.api.transfer.v1");
24671
24836
  if (this.localGateway?.supportsCarrier === true) {
24672
- 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");
24673
24843
  }
24674
24844
  if (this.fileViewerHost?.() !== void 0) capabilities.push("fileviewer.read.v1");
24675
24845
  if (this.codex.isAvailable()) capabilities.push("codex.appserver.v1", "codex.appserver.transfer.v1");