@codexhost/cli-win32-x64 0.2.3 → 0.2.4

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/README.md CHANGED
@@ -7,7 +7,7 @@ Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install -g @codexhost/cli@0.2.3
10
+ npm install -g @codexhost/cli@0.2.4
11
11
  ```
12
12
 
13
13
  Do not install this package directly. npm selects it through the optional dependencies of `@codexhost/cli`.
@@ -1 +1 @@
1
- {"schemaVersion":1,"version":"0.2.3","distribution":"npm","target":"windows-x64"}
1
+ {"schemaVersion":1,"version":"0.2.4","distribution":"npm","target":"windows-x64"}
@@ -329,7 +329,6 @@ var ELECTRON_MODULE_EXPRESSION = `(() => {
329
329
  return createRequire(process.execPath)('electron');
330
330
  })()`;
331
331
  var CONNECT_APP_HOST_CHANNEL = "codex_desktop:connect-app-host";
332
- var REVIEWED_TITLE_SERVICE_IDENTITIES = ["Dhe", "Nye", "wbe", "nxe", "tTe"];
333
332
  var POLICY_STATE_SYMBOL = "codexhost.main-process-title-policy.v1";
334
333
  var SERVICE_OWNER_SYMBOL = "codexhost.main-process-title-policy.owner.v1";
335
334
  var RENDERER_READY_EXPRESSION = "(() => { Object.defineProperty(window, '__codexhostMainProcessTitlePolicyV1', { configurable: true, value: { state: 'ready' } }); return 'ready'; })()";
@@ -363,18 +362,6 @@ var INSTALL_POLICY_FUNCTION = `async function (rendererWebContentsId) {
363
362
  ) {
364
363
  throw new Error('ThreadMetadataGenerationService signature mismatch');
365
364
  }
366
- const rawServiceClass = sampleService?.constructor?.name;
367
- const serviceClass = typeof rawServiceClass === 'string' && /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.test(rawServiceClass)
368
- ? rawServiceClass
369
- : 'unknown';
370
- const warnings = ${JSON.stringify(REVIEWED_TITLE_SERVICE_IDENTITIES)}.includes(serviceClass)
371
- ? []
372
- : [{
373
- capability: 'title-isolation',
374
- reason: 'unreviewed-title-service-identity',
375
- observedIdentity: serviceClass,
376
- }];
377
-
378
365
  const counters = {
379
366
  codexTitleCalls: 0,
380
367
  piTitleSkips: 0,
@@ -448,7 +435,6 @@ var INSTALL_POLICY_FUNCTION = `async function (rendererWebContentsId) {
448
435
  state: 'ready',
449
436
  reason: 'ready',
450
437
  requiresRendererReload: true,
451
- warnings,
452
438
  };
453
439
  }`;
454
440
  async function installMainProcessTitlePolicy(inspector, rendererWebContentsId) {
@@ -521,9 +507,7 @@ async function installMainProcessTitlePolicy(inspector, rendererWebContentsId) {
521
507
  );
522
508
  const remoteResult = installResponse.result;
523
509
  const value = isRecord2(remoteResult) ? remoteResult.value : null;
524
- if (!isRecord2(value) || value.state !== "ready" || value.reason !== "ready" || value.requiresRendererReload !== true || !Array.isArray(value.warnings) || value.warnings.length > 1 || value.warnings.some(
525
- (warning) => !isRecord2(warning) || warning.capability !== "title-isolation" || warning.reason !== "unreviewed-title-service-identity" || typeof warning.observedIdentity !== "string" || !/^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.test(warning.observedIdentity) || Object.keys(warning).length !== 3
526
- )) {
510
+ if (!isRecord2(value) || value.state !== "ready" || value.reason !== "ready" || value.requiresRendererReload !== true || Object.keys(value).length !== 3) {
527
511
  throw new Error("Main-process title policy returned an invalid status");
528
512
  }
529
513
  return value;
@@ -574,10 +558,11 @@ async function readMainProcessTitlePolicyCounters(inspector) {
574
558
  }
575
559
 
576
560
  // packages/desktop-control/src/renderer-draft-prewarm-runtime.ts
577
- function installDraftPrewarmPolicyBridge(bridge, hostId, target) {
561
+ function installDraftPrewarmPolicyBridge(bridge, hostId, target, prewarmedThreadManager) {
578
562
  const existing = target.__codexhostDraftPrewarmPolicyV1;
579
563
  existing?.dispose?.();
580
564
  const originalSend = bridge.sendRequest;
565
+ const originalPrewarm = bridge.prewarmThreadStart;
581
566
  let selectedModel = null;
582
567
  let clearInFlight = null;
583
568
  const isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -598,15 +583,24 @@ function installDraftPrewarmPolicyBridge(bridge, hostId, target) {
598
583
  };
599
584
  const routeThreadStart = (method, parameters) => {
600
585
  if (selectedModel === null || !isRecord5(parameters)) return parameters;
601
- const threadParams = method === "prewarm-thread-start-for-host" ? parameters.params : parameters.method === "thread/start" ? parameters.params : null;
586
+ const direct = method === "thread/start";
587
+ const wrapped = method === "prewarm-thread-start-for-host" || method === "send-cli-request-for-host";
588
+ if (!direct && !wrapped) return parameters;
589
+ const threadParams = direct ? parameters : parameters.params;
602
590
  if (!isRecord5(threadParams) || threadParams.ephemeral === true) return parameters;
591
+ if (direct) return { ...threadParams, model: selectedModel };
603
592
  return { ...parameters, params: { ...threadParams, model: selectedModel } };
604
593
  };
605
594
  const routedSend = (method, parameters, options) => {
606
- const routedParameters = method === "start-conversation" || method === "prewarm-conversation-for-host" ? routeCollaborationMode(method, parameters) : method === "prewarm-thread-start-for-host" || method === "send-cli-request-for-host" ? routeThreadStart(method, parameters) : parameters;
595
+ const routedParameters = method === "start-conversation" || method === "prewarm-conversation-for-host" ? routeCollaborationMode(method, parameters) : routeThreadStart(method, parameters);
607
596
  return options === void 0 ? originalSend.call(bridge, method, routedParameters) : originalSend.call(bridge, method, routedParameters, options);
608
597
  };
598
+ const routedPrewarm = (parameters, options) => {
599
+ const routedParameters = routeThreadStart("thread/start", parameters);
600
+ return options === void 0 ? originalPrewarm?.call(bridge, routedParameters) : originalPrewarm?.call(bridge, routedParameters, options);
601
+ };
609
602
  bridge.sendRequest = routedSend;
603
+ if (originalPrewarm) bridge.prewarmThreadStart = routedPrewarm;
610
604
  const policy = Object.freeze({
611
605
  state: "ready",
612
606
  select(model) {
@@ -618,6 +612,10 @@ function installDraftPrewarmPolicyBridge(bridge, hostId, target) {
618
612
  return true;
619
613
  },
620
614
  clear() {
615
+ if (prewarmedThreadManager?.discardAllPrewarmedThreads) {
616
+ prewarmedThreadManager.discardAllPrewarmedThreads();
617
+ return Promise.resolve();
618
+ }
621
619
  if (clearInFlight === null) {
622
620
  clearInFlight = Promise.resolve(
623
621
  originalSend.call(bridge, "clear-prewarmed-threads-for-host", { hostId })
@@ -629,6 +627,10 @@ function installDraftPrewarmPolicyBridge(bridge, hostId, target) {
629
627
  },
630
628
  dispose() {
631
629
  if (bridge.sendRequest === routedSend) bridge.sendRequest = originalSend;
630
+ if (bridge.prewarmThreadStart === routedPrewarm) {
631
+ if (originalPrewarm) bridge.prewarmThreadStart = originalPrewarm;
632
+ else delete bridge.prewarmThreadStart;
633
+ }
632
634
  selectedModel = null;
633
635
  }
634
636
  });
@@ -667,77 +669,90 @@ async function installDraftPrewarmPolicyInRenderer(contents, findRequestManagerE
667
669
  const manager = managerProperties.result?.find(
668
670
  (property) => property.name === "manager"
669
671
  )?.value;
672
+ const prewarmedThreadManager = managerProperties.result?.find(
673
+ (property) => property.name === "prewarmedThreadManager"
674
+ )?.value;
670
675
  if (candidateCount !== 1 || hostId !== "local" || typeof manager?.objectId !== "string") {
671
676
  throw new Error("Renderer request manager is ambiguous");
672
677
  }
673
678
  const managerFunctions = await contents.debugger.sendCommand("Runtime.getProperties", {
674
679
  objectId: manager.objectId
675
680
  });
676
- let managerSendRequestId = managerFunctions.result?.find(
677
- (property) => property.name === "sendRequest"
678
- )?.value?.objectId;
679
- const managerPrototypeId = managerFunctions.internalProperties?.find(
680
- (property) => property.name === "[[Prototype]]"
681
- )?.value?.objectId;
682
- if (typeof managerSendRequestId !== "string" && typeof managerPrototypeId === "string") {
683
- const prototypeFunctions = await contents.debugger.sendCommand("Runtime.getProperties", {
684
- objectId: managerPrototypeId,
685
- ownProperties: true
686
- });
687
- managerSendRequestId = prototypeFunctions.result?.find(
681
+ const managerPropertyNames = new Set(
682
+ managerFunctions.result?.map((property) => String(property.name)) ?? []
683
+ );
684
+ let hostBridgeObjectId = managerPropertyNames.has("sendRequest") && managerPropertyNames.has("prewarmThreadStart") && managerPropertyNames.has("enqueueRequest") ? manager.objectId : void 0;
685
+ if (!hostBridgeObjectId) {
686
+ let managerSendRequestId = managerFunctions.result?.find(
688
687
  (property) => property.name === "sendRequest"
689
688
  )?.value?.objectId;
690
- }
691
- if (typeof managerSendRequestId !== "string") {
692
- throw new Error("Renderer request manager sendRequest is unavailable");
693
- }
694
- const functionProperties = await contents.debugger.sendCommand("Runtime.getProperties", {
695
- objectId: managerSendRequestId
696
- });
697
- const scopesId = functionProperties.internalProperties?.find(
698
- (property) => property.name === "[[Scopes]]"
699
- )?.value?.objectId;
700
- const hostBridgeCandidates = [];
701
- if (typeof scopesId === "string") {
702
- const scopes = await contents.debugger.sendCommand("Runtime.getProperties", {
703
- objectId: scopesId,
704
- ownProperties: true
689
+ const managerPrototypeId = managerFunctions.internalProperties?.find(
690
+ (property) => property.name === "[[Prototype]]"
691
+ )?.value?.objectId;
692
+ if (typeof managerSendRequestId !== "string" && typeof managerPrototypeId === "string") {
693
+ const prototypeFunctions = await contents.debugger.sendCommand("Runtime.getProperties", {
694
+ objectId: managerPrototypeId,
695
+ ownProperties: true
696
+ });
697
+ managerSendRequestId = prototypeFunctions.result?.find(
698
+ (property) => property.name === "sendRequest"
699
+ )?.value?.objectId;
700
+ }
701
+ if (typeof managerSendRequestId !== "string") {
702
+ throw new Error("Renderer request manager sendRequest is unavailable");
703
+ }
704
+ const functionProperties = await contents.debugger.sendCommand("Runtime.getProperties", {
705
+ objectId: managerSendRequestId
705
706
  });
706
- for (const scope of scopes.result ?? []) {
707
- if (typeof scope.value?.objectId !== "string") continue;
708
- const scopeProperties = await contents.debugger.sendCommand("Runtime.getProperties", {
709
- objectId: scope.value.objectId,
707
+ const scopesId = functionProperties.internalProperties?.find(
708
+ (property) => property.name === "[[Scopes]]"
709
+ )?.value?.objectId;
710
+ const hostBridgeCandidates = [];
711
+ if (typeof scopesId === "string") {
712
+ const scopes = await contents.debugger.sendCommand("Runtime.getProperties", {
713
+ objectId: scopesId,
710
714
  ownProperties: true
711
715
  });
712
- for (const property of scopeProperties.result ?? []) {
713
- if (property.value?.type !== "object" || typeof property.value.objectId !== "string") {
714
- continue;
715
- }
716
- const candidateSignature = await contents.debugger.sendCommand(
717
- "Runtime.callFunctionOn",
718
- {
719
- objectId: property.value.objectId,
720
- functionDeclaration: "function(){const send=this.sendRequest;return typeof send==='function'&&Function.prototype.toString.call(send).includes('messageHandler')}",
721
- returnByValue: true
716
+ for (const scope of scopes.result ?? []) {
717
+ if (typeof scope.value?.objectId !== "string") continue;
718
+ const scopeProperties = await contents.debugger.sendCommand("Runtime.getProperties", {
719
+ objectId: scope.value.objectId,
720
+ ownProperties: true
721
+ });
722
+ for (const property of scopeProperties.result ?? []) {
723
+ if (property.value?.type !== "object" || typeof property.value.objectId !== "string") {
724
+ continue;
725
+ }
726
+ const candidateSignature = await contents.debugger.sendCommand(
727
+ "Runtime.callFunctionOn",
728
+ {
729
+ objectId: property.value.objectId,
730
+ functionDeclaration: "function(){const send=this.sendRequest;return typeof send==='function'&&Function.prototype.toString.call(send).includes('messageHandler')}",
731
+ returnByValue: true
732
+ }
733
+ );
734
+ if (candidateSignature.result?.value === true) {
735
+ hostBridgeCandidates.push({
736
+ name: String(property.name),
737
+ objectId: property.value.objectId
738
+ });
722
739
  }
723
- );
724
- if (candidateSignature.result?.value === true) {
725
- hostBridgeCandidates.push({
726
- name: String(property.name),
727
- objectId: property.value.objectId
728
- });
729
740
  }
730
741
  }
731
742
  }
732
- }
733
- const hostBridge = hostBridgeCandidates[0];
734
- if (hostBridgeCandidates.length !== 1 || !hostBridge) {
735
- throw new Error("Renderer Host request bridge is ambiguous");
743
+ const hostBridge = hostBridgeCandidates[0];
744
+ if (hostBridgeCandidates.length !== 1 || !hostBridge) {
745
+ throw new Error("Renderer Host request bridge is ambiguous");
746
+ }
747
+ hostBridgeObjectId = hostBridge.objectId;
736
748
  }
737
749
  const installed = await contents.debugger.sendCommand("Runtime.callFunctionOn", {
738
- objectId: hostBridge.objectId,
750
+ objectId: hostBridgeObjectId,
739
751
  functionDeclaration: installRendererPolicyFunction,
740
- arguments: [{ value: hostId }],
752
+ arguments: [
753
+ { value: hostId },
754
+ ...typeof prewarmedThreadManager?.objectId === "string" ? [{ objectId: prewarmedThreadManager.objectId }] : []
755
+ ],
741
756
  awaitPromise: true,
742
757
  returnByValue: true
743
758
  });
@@ -777,24 +792,31 @@ var FIND_REQUEST_MANAGER_EXPRESSION = `(() => {
777
792
  typeof value === 'object' &&
778
793
  value.requestClient != null &&
779
794
  typeof value.requestClient.prewarmThreadStart === 'function' &&
780
- typeof value.sendRequest === 'function' &&
781
- Function.prototype.toString.call(value.sendRequest).includes(
782
- 'send-cli-request-for-host',
783
- )
795
+ typeof value.requestClient.sendRequest === 'function' &&
796
+ typeof value.requestClient.enqueueRequest === 'function' &&
797
+ typeof value.sendRequest === 'function'
784
798
  ) {
785
799
  managers.add(value);
786
800
  }
787
801
  }
788
802
  }
789
803
  const manager = managers.size === 1 ? managers.values().next().value : null;
804
+ const requestClient =
805
+ manager != null &&
806
+ typeof manager.requestClient?.sendRequest === 'function' &&
807
+ typeof manager.requestClient?.prewarmThreadStart === 'function' &&
808
+ typeof manager.requestClient?.enqueueRequest === 'function'
809
+ ? manager.requestClient
810
+ : manager;
790
811
  return {
791
812
  candidateCount: managers.size,
792
- hostId: manager?.getHostId?.() ?? null,
793
- manager,
813
+ hostId: manager?.getHostId?.() ?? requestClient?.hostId ?? null,
814
+ manager: requestClient,
815
+ prewarmedThreadManager: manager?.prewarmedThreadManager ?? null,
794
816
  };
795
817
  })()`;
796
- var INSTALL_RENDERER_POLICY_FUNCTION = `function(hostId) {
797
- return (${installDraftPrewarmPolicyBridge.toString()})(this, hostId, window);
818
+ var INSTALL_RENDERER_POLICY_FUNCTION = `function(hostId, prewarmedThreadManager) {
819
+ return (${installDraftPrewarmPolicyBridge.toString()})(this, hostId, window, prewarmedThreadManager);
798
820
  }`;
799
821
  var REQUEST_MANAGER_WAIT_TIMEOUT_MS = 6e4;
800
822
  var REQUEST_MANAGER_POLL_INTERVAL_MS = 25;
@@ -1252,9 +1274,6 @@ function startupTrace2(stage, detail) {
1252
1274
  }
1253
1275
  function validCompatibilityIssue(state, issue) {
1254
1276
  const keys = Object.keys(issue);
1255
- if (state === "compatible-with-warning") {
1256
- return issue.capability === "title-isolation" && issue.reason === "unreviewed-title-service-identity" && typeof issue.observedIdentity === "string" && /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.test(issue.observedIdentity) && keys.length === 3;
1257
- }
1258
1277
  if (state === "degraded") {
1259
1278
  return [
1260
1279
  "permission-control",
@@ -1262,12 +1281,12 @@ function validCompatibilityIssue(state, issue) {
1262
1281
  "fork-control",
1263
1282
  "usage-surface",
1264
1283
  "settings-surface"
1265
- ].includes(issue.capability) && issue.reason === "capability-unavailable" && issue.observedIdentity === void 0 && keys.length === 2;
1284
+ ].includes(issue.capability) && issue.reason === "capability-unavailable" && keys.length === 2;
1266
1285
  }
1267
1286
  return false;
1268
1287
  }
1269
1288
  function serializeDesktopControllerReadiness(readiness) {
1270
- if (readiness.schemaVersion !== 2 || !["compatible", "compatible-with-warning", "degraded"].includes(readiness.state) || !Array.isArray(readiness.issues) || readiness.issues.length > 1 || readiness.state === "compatible" && readiness.issues.length !== 0 || readiness.state !== "compatible" && (readiness.issues.length !== 1 || !readiness.issues[0] || !validCompatibilityIssue(readiness.state, readiness.issues[0])) || Object.keys(readiness).length !== 3) {
1289
+ if (readiness.schemaVersion !== 2 || !["compatible", "degraded"].includes(readiness.state) || !Array.isArray(readiness.issues) || readiness.issues.length > 1 || readiness.state === "compatible" && readiness.issues.length !== 0 || readiness.state !== "compatible" && (readiness.issues.length !== 1 || !readiness.issues[0] || !validCompatibilityIssue(readiness.state, readiness.issues[0])) || Object.keys(readiness).length !== 3) {
1271
1290
  throw new Error("Desktop Controller readiness is invalid");
1272
1291
  }
1273
1292
  const line = JSON.stringify(readiness);
@@ -1476,12 +1495,11 @@ ${rendererSource}`,
1476
1495
  })
1477
1496
  });
1478
1497
  startupTrace2("attachment server ready");
1479
- const issues = session?.snapshot.titlePolicy.warnings ?? [];
1480
1498
  startupTrace2("publishing readiness");
1481
1499
  dependencies.ready({
1482
1500
  schemaVersion: 2,
1483
- state: issues.length === 0 ? "compatible" : "compatible-with-warning",
1484
- issues
1501
+ state: "compatible",
1502
+ issues: []
1485
1503
  });
1486
1504
  while (!signal.aborted) {
1487
1505
  await dependencies.sleep(dependencies.monitorIntervalMs);
@@ -45148,13 +45148,13 @@ function resolveExecutable(command, environment) {
45148
45148
  function resolveDeepSeekCommand(configured, environment) {
45149
45149
  if (configured) {
45150
45150
  const command = resolveExecutable(configured, environment);
45151
- return command ? { command, arguments: [] } : null;
45151
+ return command ? { command, arguments: [], kind: "configured" } : null;
45152
45152
  }
45153
45153
  const dsh = resolveExecutable("dsh", environment);
45154
45154
  if (dsh)
45155
- return { command: dsh, arguments: [] };
45155
+ return { command: dsh, arguments: [], kind: "dsh" };
45156
45156
  const npx = resolveExecutable(process.platform === "win32" ? "npx.cmd" : "npx", environment);
45157
- return npx ? { command: npx, arguments: ["--no-install", "@deepseek-ai/dsh"] } : null;
45157
+ return npx ? { command: npx, arguments: ["--no-install", "@deepseek-ai/dsh"], kind: "npx" } : null;
45158
45158
  }
45159
45159
  function unwrap(response, operation) {
45160
45160
  if (response.result.ok)
@@ -45247,6 +45247,9 @@ var DeepSeekHostConnection = class {
45247
45247
  if (processError)
45248
45248
  throw processError;
45249
45249
  if (child.exitCode !== null || child.signalCode !== null) {
45250
+ if (invocation.kind === "npx") {
45251
+ throw new DeepSeekHarnessTransportError("notInstalled", "DeepSeek Harness package is not installed");
45252
+ }
45250
45253
  throw new DeepSeekHarnessTransportError("processExited", "DeepSeek Harness Web exited during startup");
45251
45254
  }
45252
45255
  try {
@@ -17275,14 +17275,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17275
17275
  var SIDEBAR_THREAD_ID_ATTRIBUTE = "data-app-action-sidebar-thread-id";
17276
17276
  var SIDEBAR_THREAD_HOST_ID_ATTRIBUTE = "data-app-action-sidebar-thread-host-id";
17277
17277
  var SIDEBAR_AGENT_ICON_ATTRIBUTE = "data-codexhost-sidebar-agent-icon";
17278
+ var OWNERSHIP_RETRY_DELAYS_MS = [100, 300, 800, 1500, 3e3];
17278
17279
  function isRecord2(value) {
17279
17280
  return typeof value === "object" && value !== null && !Array.isArray(value);
17280
17281
  }
17281
- function threadIdFromSidebarRowElement(element) {
17282
+ function sidebarThreadAttributes(element) {
17282
17283
  const taskKey = element.getAttribute(SIDEBAR_THREAD_ID_ATTRIBUTE);
17283
17284
  const hostId = element.getAttribute(SIDEBAR_THREAD_HOST_ID_ATTRIBUTE);
17284
17285
  const rowMarker = element.getAttribute(SIDEBAR_THREAD_ROW_ATTRIBUTE);
17285
17286
  if (taskKey === null || hostId === null || rowMarker === null) return null;
17287
+ return { taskKey, hostId, rowMarker };
17288
+ }
17289
+ function draftIdFromSidebarRowElement(element) {
17290
+ const attributes = sidebarThreadAttributes(element);
17291
+ if (!attributes) return null;
17292
+ const hostPrefix = `${attributes.hostId}:`;
17293
+ const taskKey = attributes.taskKey.startsWith(hostPrefix) ? attributes.taskKey.slice(hostPrefix.length) : attributes.taskKey;
17294
+ return taskKey.startsWith("client-new-thread:") ? taskKey : null;
17295
+ }
17296
+ function threadIdFromSidebarRowElement(element) {
17297
+ const attributes = sidebarThreadAttributes(element);
17298
+ if (!attributes) return null;
17299
+ const { taskKey, hostId, rowMarker } = attributes;
17286
17300
  const fiberNames = Object.getOwnPropertyNames(element).filter(
17287
17301
  (name) => name.startsWith("__reactFiber$")
17288
17302
  );
@@ -17324,6 +17338,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17324
17338
  threadId() {
17325
17339
  return threadIdFromSidebarRowElement(this.element);
17326
17340
  }
17341
+ draftId() {
17342
+ return draftIdFromSidebarRowElement(this.element);
17343
+ }
17327
17344
  render(agent) {
17328
17345
  const titleTrigger = this.element.querySelector("[data-thread-title-trigger]");
17329
17346
  const title = titleTrigger?.querySelector("[data-thread-title]");
@@ -17403,6 +17420,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17403
17420
  const ownershipByThread = /* @__PURE__ */ new Map();
17404
17421
  const pending = /* @__PURE__ */ new Set();
17405
17422
  const failed = /* @__PURE__ */ new Set();
17423
+ const provisionalCodex = /* @__PURE__ */ new Set();
17424
+ const ownershipRetryAttempts = /* @__PURE__ */ new Map();
17425
+ const ownershipRetryTimers = /* @__PURE__ */ new Map();
17406
17426
  let disposed = false;
17407
17427
  let scanScheduled = false;
17408
17428
  const scheduleScan = () => {
@@ -17410,6 +17430,30 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17410
17430
  scanScheduled = true;
17411
17431
  queueMicrotask(scan);
17412
17432
  };
17433
+ const clearOwnershipRetry = (threadId) => {
17434
+ const timer = ownershipRetryTimers.get(threadId);
17435
+ if (timer !== void 0) clearTimeout(timer);
17436
+ ownershipRetryTimers.delete(threadId);
17437
+ ownershipRetryAttempts.delete(threadId);
17438
+ provisionalCodex.delete(threadId);
17439
+ };
17440
+ const scheduleOwnershipRetry = (threadId) => {
17441
+ if (disposed || !provisionalCodex.has(threadId) || pending.has(threadId) || ownershipRetryTimers.has(threadId)) {
17442
+ return;
17443
+ }
17444
+ const attempt = ownershipRetryAttempts.get(threadId) ?? 0;
17445
+ const delay = OWNERSHIP_RETRY_DELAYS_MS[attempt];
17446
+ if (delay === void 0) return;
17447
+ ownershipRetryAttempts.set(threadId, attempt + 1);
17448
+ const timer = setTimeout(() => {
17449
+ ownershipRetryTimers.delete(threadId);
17450
+ if (disposed) return;
17451
+ failed.delete(threadId);
17452
+ ownershipByThread.delete(threadId);
17453
+ scheduleScan();
17454
+ }, delay);
17455
+ ownershipRetryTimers.set(threadId, timer);
17456
+ };
17413
17457
  const requestOwnership = (threadIds, client) => {
17414
17458
  for (const threadId of threadIds) pending.add(threadId);
17415
17459
  let succeeded = false;
@@ -17418,6 +17462,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17418
17462
  for (const ownership of threads) {
17419
17463
  ownershipByThread.set(ownership.threadId, rendererAgentForThreadOwnership(ownership));
17420
17464
  failed.delete(ownership.threadId);
17465
+ if (ownership.owner === "codex") {
17466
+ provisionalCodex.add(ownership.threadId);
17467
+ scheduleOwnershipRetry(ownership.threadId);
17468
+ } else {
17469
+ clearOwnershipRetry(ownership.threadId);
17470
+ }
17421
17471
  }
17422
17472
  succeeded = true;
17423
17473
  }).catch(() => {
@@ -17425,6 +17475,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17425
17475
  for (const threadId of threadIds) failed.add(threadId);
17426
17476
  }).finally(() => {
17427
17477
  for (const threadId of threadIds) pending.delete(threadId);
17478
+ for (const threadId of threadIds) scheduleOwnershipRetry(threadId);
17428
17479
  if (succeeded) scheduleScan();
17429
17480
  });
17430
17481
  };
@@ -17433,8 +17484,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17433
17484
  if (disposed) return;
17434
17485
  const unresolved = /* @__PURE__ */ new Set();
17435
17486
  for (const row of dom.rows()) {
17487
+ if (!row.isConnected()) {
17488
+ row.clear();
17489
+ continue;
17490
+ }
17436
17491
  const threadId = hostThreadIdSchema.safeParse(row.threadId());
17437
- if (!row.isConnected() || !threadId.success) {
17492
+ const localAgent = options.getLocalAgent?.({
17493
+ threadId: threadId.success ? threadId.data : null,
17494
+ draftId: row.draftId()
17495
+ });
17496
+ if (localAgent !== null && localAgent !== void 0) {
17497
+ if (threadId.success) {
17498
+ ownershipByThread.set(threadId.data, localAgent === "codex" ? null : localAgent);
17499
+ clearOwnershipRetry(threadId.data);
17500
+ }
17501
+ if (localAgent === "codex") row.clear();
17502
+ else row.render(localAgent);
17503
+ continue;
17504
+ }
17505
+ if (!threadId.success) {
17438
17506
  row.clear();
17439
17507
  continue;
17440
17508
  }
@@ -17461,6 +17529,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17461
17529
  return {
17462
17530
  refresh() {
17463
17531
  failed.clear();
17532
+ for (const threadId of provisionalCodex) {
17533
+ const timer = ownershipRetryTimers.get(threadId);
17534
+ if (timer !== void 0) clearTimeout(timer);
17535
+ ownershipRetryTimers.delete(threadId);
17536
+ ownershipRetryAttempts.delete(threadId);
17537
+ ownershipByThread.delete(threadId);
17538
+ }
17464
17539
  scheduleScan();
17465
17540
  },
17466
17541
  dispose() {
@@ -17471,6 +17546,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17471
17546
  ownershipByThread.clear();
17472
17547
  pending.clear();
17473
17548
  failed.clear();
17549
+ provisionalCodex.clear();
17550
+ for (const timer of ownershipRetryTimers.values()) clearTimeout(timer);
17551
+ ownershipRetryTimers.clear();
17552
+ ownershipRetryAttempts.clear();
17474
17553
  }
17475
17554
  };
17476
17555
  }
@@ -17916,6 +17995,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17916
17995
  return source.includes("send-cli-request-for-host") && hasPrewarmMethod(value.requestClient);
17917
17996
  }
17918
17997
  function matchesCurrentPrewarmSignature(target) {
17998
+ const bridge = target.requestClient ?? target;
17999
+ const stableApiShape = bridge.hostId === "local" && typeof bridge.sendRequest === "function" && typeof bridge.prewarmThreadStart === "function" && typeof bridge.enqueueRequest === "function";
18000
+ if (stableApiShape) return true;
17919
18001
  const prewarm = target.prewarmThreadStart ?? target.requestClient?.prewarmThreadStart;
17920
18002
  if (!prewarm) return false;
17921
18003
  const source = Function.prototype.toString.call(prewarm);
@@ -20177,8 +20259,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20177
20259
  let applyAdapterAgent = null;
20178
20260
  let modelControl = null;
20179
20261
  let usageNotificationDispose = null;
20262
+ const localAgentForSidebarThread = (input) => {
20263
+ for (const mounted of mountedByComposer.values()) {
20264
+ const target = mounted.modelTarget;
20265
+ if (target?.[0] === "default" && input.draftId !== null && target[1] === input.draftId) {
20266
+ return controller.get(mounted.composer).agent;
20267
+ }
20268
+ if (target?.[0] === "conversation" && input.threadId !== null && target[1] === input.threadId && mounted.ownershipStatus === "ready") {
20269
+ return controller.get(mounted.composer).agent;
20270
+ }
20271
+ }
20272
+ return null;
20273
+ };
20180
20274
  const sidebarAgentIcons = installRendererSidebarAgentIcons({
20181
- getClient: () => modelControl
20275
+ getClient: () => modelControl,
20276
+ getLocalAgent: localAgentForSidebarThread
20182
20277
  });
20183
20278
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
20184
20279
  getUpdateClient: () => modelControl
@@ -20194,6 +20289,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20194
20289
  );
20195
20290
  let availabilityRequestGeneration = 0;
20196
20291
  let availabilityRequest = null;
20292
+ let availabilityRetryTimer = null;
20293
+ let availabilityRetryAttempt = 0;
20294
+ const availabilityRetryDelays = [500, 1e3, 2e3, 4e3, 8e3];
20197
20295
  const usageRefreshTimers = /* @__PURE__ */ new Map();
20198
20296
  const usageRefreshAttempts = /* @__PURE__ */ new Map();
20199
20297
  const isMountedComposer = (composer) => composer.isConnected && composer.matches(CODEX_COMPOSER_SELECTOR) && mountedByComposer.has(composer);
@@ -20366,6 +20464,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20366
20464
  } finally {
20367
20465
  if (isCurrentOwnershipRequest(mounted, generation)) {
20368
20466
  renderMounted(mounted);
20467
+ sidebarAgentIcons.refresh();
20369
20468
  if (mounted.ownershipStatus !== "error" && shouldRetryExternalThreadUsage(
20370
20469
  controller.get(mounted.composer).agent,
20371
20470
  mounted.usage,
@@ -20411,6 +20510,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20411
20510
  if (previousTarget?.[0] === "conversation") renderMounted(mounted);
20412
20511
  void loadThreadOwnership(mounted);
20413
20512
  }
20513
+ sidebarAgentIcons.refresh();
20414
20514
  return true;
20415
20515
  };
20416
20516
  const loadExternalCatalog = async (mounted) => {
@@ -20921,6 +21021,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20921
21021
  mounted.modelView = { status: "idle" };
20922
21022
  mounted.permissionModeView = { status: "idle" };
20923
21023
  }
21024
+ sidebarAgentIcons.refresh();
20924
21025
  return switched;
20925
21026
  } catch {
20926
21027
  adapterStatus = {
@@ -20940,7 +21041,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20940
21041
  const url2 = RENDERER_AGENT_INSTALL_URLS[agent];
20941
21042
  window.open(url2, "_blank", "noopener,noreferrer");
20942
21043
  };
20943
- const refreshHarnessAvailability = (refresh = false) => {
21044
+ const resetHarnessAvailabilityRetry = () => {
21045
+ if (availabilityRetryTimer !== null) {
21046
+ window.clearTimeout(availabilityRetryTimer);
21047
+ availabilityRetryTimer = null;
21048
+ }
21049
+ availabilityRetryAttempt = 0;
21050
+ };
21051
+ const scheduleHarnessAvailabilityRetry = () => {
21052
+ if (disposed || availabilityRetryTimer !== null || availabilityRetryAttempt >= availabilityRetryDelays.length) {
21053
+ return;
21054
+ }
21055
+ const delay = availabilityRetryDelays[availabilityRetryAttempt];
21056
+ availabilityRetryAttempt += 1;
21057
+ availabilityRetryTimer = window.setTimeout(() => {
21058
+ availabilityRetryTimer = null;
21059
+ void refreshHarnessAvailability(true, true);
21060
+ }, delay);
21061
+ };
21062
+ const refreshHarnessAvailability = (refresh = false, retry = false) => {
21063
+ if (!retry) resetHarnessAvailabilityRetry();
20944
21064
  if (!modelControl) return Promise.resolve();
20945
21065
  const client = modelControl;
20946
21066
  if (availabilityRequest?.client === client) return availabilityRequest.promise;
@@ -20982,6 +21102,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20982
21102
  }
20983
21103
  })
20984
21104
  );
21105
+ if (externalAgents.every((agent) => harnessAvailability[agent] === "ready")) {
21106
+ resetHarnessAvailabilityRetry();
21107
+ } else {
21108
+ scheduleHarnessAvailabilityRetry();
21109
+ }
20985
21110
  })();
20986
21111
  const request = { client, promise: promise2 };
20987
21112
  availabilityRequest = request;
@@ -21060,6 +21185,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21060
21185
  );
21061
21186
  }
21062
21187
  renderMounted(mounted);
21188
+ sidebarAgentIcons.refresh();
21063
21189
  if (threadIdFromComposerModelTarget(modelTarget) && !inherited) {
21064
21190
  void loadThreadOwnership(mounted);
21065
21191
  } else if (threadIdFromComposerModelTarget(modelTarget) && inherited && shouldRetryExternalThreadUsage(state.agent, mounted.usage, mounted.accountCredits)) {
@@ -21384,6 +21510,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21384
21510
  document.removeEventListener("click", onClick, true);
21385
21511
  window.removeEventListener("codexhost:renderer-adapter-status", onAdapterStatus);
21386
21512
  window.removeEventListener("focus", onWindowFocus);
21513
+ resetHarnessAvailabilityRetry();
21387
21514
  for (const timer of usageRefreshTimers.values()) window.clearTimeout(timer);
21388
21515
  usageRefreshTimers.clear();
21389
21516
  for (const mounted of mountedByComposer.values()) {
package/bin/codexhost.exe CHANGED
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexhost/cli-win32-x64",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.",
5
5
  "type": "module",
6
6
  "files": [