@codexhost/cli-win32-arm64 0.3.1 → 0.3.3

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.3.1
10
+ npm install -g @codexhost/cli@0.3.3
11
11
  ```
12
12
 
13
13
  Do not install this package directly. npm selects it through the optional dependencies of `@codexhost/cli`.
@@ -22,6 +22,8 @@ codexhost --version
22
22
  codexhost inspect
23
23
  codexhost launch
24
24
  codexhost remote install
25
+ codexhost remote start
26
+ codexhost remote stop
25
27
  codexhost remote status
26
28
  codexhost remote uninstall
27
29
  ```
@@ -1 +1 @@
1
- {"schemaVersion":1,"version":"0.3.1","distribution":"npm","target":"windows-arm64"}
1
+ {"schemaVersion":1,"version":"0.3.3","distribution":"npm","target":"windows-arm64"}
@@ -558,16 +558,376 @@ async function readMainProcessTitlePolicyCounters(inspector) {
558
558
  }
559
559
 
560
560
  // packages/desktop-control/src/renderer-draft-prewarm-runtime.ts
561
- function installDraftPrewarmPolicyBridge(bridge, hostId, target, prewarmedThreadManager) {
561
+ function installDraftPrewarmPolicyBridge(manager, bridge, hostId, target, prewarmedThreadManager) {
562
562
  const existing = target.__codexhostDraftPrewarmPolicyV1;
563
- if (existing?.owns?.length === 3 && existing.owns(bridge, hostId, prewarmedThreadManager) === true) {
563
+ if (existing?.owns?.length === 4 && existing.owns(manager, bridge, hostId, prewarmedThreadManager) === true) {
564
564
  return { state: "ready", reason: "owned-request-bridge" };
565
565
  }
566
566
  existing?.dispose?.();
567
567
  const originalSend = bridge.sendRequest;
568
568
  const originalPrewarm = bridge.prewarmThreadStart;
569
+ const originalOnNotification = manager.onNotification;
570
+ const originalDispatchAppServerResponse = manager.dispatchAppServerResponse;
569
571
  let selectedModel = null;
570
572
  const isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
573
+ const isRemoteControlHost = hostId.startsWith("remote-control:");
574
+ const knownExternalThreadIds = /* @__PURE__ */ new Set();
575
+ const knownOfficialThreadIds = /* @__PURE__ */ new Set();
576
+ const threadOwnershipResolutions = /* @__PURE__ */ new Map();
577
+ const createBridgeProcessHandle = () => `codexhost-${typeof crypto?.randomUUID === "function" ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(16).slice(2)}`}`;
578
+ let bridgeProcessHandle = createBridgeProcessHandle();
579
+ const bridgeReadyMethod = "codexhost/remote-control-bridge/ready";
580
+ const bridgeRequests = /* @__PURE__ */ new Map();
581
+ const bridgeServerRequestIdPrefix = "codexhost/remote-control-bridge/server-request/";
582
+ const bridgeServerRequests = /* @__PURE__ */ new Map();
583
+ let nextBridgeServerRequestOrdinal = 1;
584
+ let outputDecoders = {
585
+ stdout: new TextDecoder(),
586
+ stderr: new TextDecoder()
587
+ };
588
+ const outputBuffers = { stdout: "", stderr: "" };
589
+ let bridgeState = "idle";
590
+ let bridgeReadyPromise = null;
591
+ let bridgeReadyResolve = null;
592
+ let bridgeReadyReject = null;
593
+ let bridgeReadyTimeout = null;
594
+ let bridgeInitialization = null;
595
+ let writeTail = Promise.resolve();
596
+ const transportError = (message, cause) => {
597
+ const error = new Error(`codexhost Remote Control bridge: ${message}`);
598
+ if (cause !== void 0) Object.assign(error, { cause });
599
+ return error;
600
+ };
601
+ const utf8Base64 = (value) => {
602
+ const bytes = new TextEncoder().encode(value);
603
+ let binary = "";
604
+ for (let index = 0; index < bytes.length; index += 32768) {
605
+ binary += String.fromCharCode(...bytes.subarray(index, index + 32768));
606
+ }
607
+ return btoa(binary);
608
+ };
609
+ const utf16LeBase64 = (value) => {
610
+ let binary = "";
611
+ for (let index = 0; index < value.length; index += 1) {
612
+ const codeUnit = value.charCodeAt(index);
613
+ binary += String.fromCharCode(codeUnit & 255, codeUnit >>> 8);
614
+ }
615
+ return btoa(binary);
616
+ };
617
+ const failBridge = (cause, terminateProcess = true) => {
618
+ if (bridgeState === "failed" || bridgeState === "disposed") return;
619
+ const failedProcessHandle = bridgeProcessHandle;
620
+ const terminate = terminateProcess && (bridgeState === "starting" || bridgeState === "ready");
621
+ bridgeState = "failed";
622
+ if (bridgeReadyTimeout !== null) globalThis.clearTimeout(bridgeReadyTimeout);
623
+ bridgeReadyTimeout = null;
624
+ const stderr = outputBuffers.stderr.trim().slice(-1e3);
625
+ const error = transportError(
626
+ `${cause instanceof Error ? cause.message : String(cause)}${stderr ? `; ${stderr}` : ""}`,
627
+ cause
628
+ );
629
+ bridgeReadyReject?.(error);
630
+ bridgeReadyReject = null;
631
+ bridgeReadyResolve = null;
632
+ for (const requestId of bridgeRequests.keys()) bridge.onError(requestId, error);
633
+ bridgeRequests.clear();
634
+ if (isRemoteControlHost && terminate) {
635
+ void Promise.resolve(
636
+ originalSend.call(bridge, "process/kill", { processHandle: failedProcessHandle })
637
+ ).catch(() => void 0);
638
+ }
639
+ };
640
+ const resetFailedBridge = () => {
641
+ if (bridgeState !== "failed") return;
642
+ bridgeProcessHandle = createBridgeProcessHandle();
643
+ outputDecoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
644
+ outputBuffers.stdout = "";
645
+ outputBuffers.stderr = "";
646
+ bridgeReadyPromise = null;
647
+ bridgeReadyResolve = null;
648
+ bridgeReadyReject = null;
649
+ bridgeInitialization = null;
650
+ writeTail = Promise.resolve();
651
+ bridgeServerRequests.clear();
652
+ nextBridgeServerRequestOrdinal = 1;
653
+ bridgeState = "idle";
654
+ };
655
+ const rememberExternalThread = (value) => {
656
+ if (!isRecord5(value) || typeof value.id !== "string") return;
657
+ if (value.modelProvider === "codexhost" || value.cliVersion === "codexhost") {
658
+ knownExternalThreadIds.add(value.id);
659
+ knownOfficialThreadIds.delete(value.id);
660
+ }
661
+ };
662
+ const observeBridgeResult = (request, result) => {
663
+ if (!request || !isRecord5(result)) return;
664
+ if (request.method === "thread/list" && Array.isArray(result.data)) {
665
+ for (const thread of result.data) rememberExternalThread(thread);
666
+ return;
667
+ }
668
+ if (request.method === "thread/start" || request.method === "thread/read" || request.method === "thread/resume") {
669
+ rememberExternalThread(result.thread);
670
+ return;
671
+ }
672
+ if (request.method === "codexhost/thread/inspect" && isRecord5(request.parameters) && typeof request.parameters.threadId === "string") {
673
+ if (result.owner === "external") {
674
+ knownExternalThreadIds.add(request.parameters.threadId);
675
+ knownOfficialThreadIds.delete(request.parameters.threadId);
676
+ } else if (result.owner === "codex") {
677
+ knownOfficialThreadIds.add(request.parameters.threadId);
678
+ knownExternalThreadIds.delete(request.parameters.threadId);
679
+ }
680
+ return;
681
+ }
682
+ if (request.method === "thread/delete" && isRecord5(request.parameters) && typeof request.parameters.threadId === "string") {
683
+ knownExternalThreadIds.delete(request.parameters.threadId);
684
+ knownOfficialThreadIds.delete(request.parameters.threadId);
685
+ }
686
+ };
687
+ const handleBridgeFrame = (value) => {
688
+ if (!isRecord5(value)) {
689
+ failBridge("received a non-object app-server frame");
690
+ return;
691
+ }
692
+ if (value.method === bridgeReadyMethod && value.id === void 0) {
693
+ if (isRecord5(value.params) && value.params.protocolVersion === 1) {
694
+ bridgeState = "ready";
695
+ if (bridgeReadyTimeout !== null) globalThis.clearTimeout(bridgeReadyTimeout);
696
+ bridgeReadyTimeout = null;
697
+ bridgeReadyResolve?.();
698
+ bridgeReadyResolve = null;
699
+ bridgeReadyReject = null;
700
+ } else {
701
+ failBridge("reported an unsupported protocol version");
702
+ }
703
+ return;
704
+ }
705
+ if (typeof value.method === "string" && value.id === void 0) {
706
+ if (value.method === "thread/started" && isRecord5(value.params)) {
707
+ rememberExternalThread(value.params.thread);
708
+ }
709
+ originalOnNotification.call(manager, value.method, value.params);
710
+ return;
711
+ }
712
+ if (typeof value.method === "string" && value.id !== void 0) {
713
+ const outerRequestId = `${bridgeServerRequestIdPrefix}${bridgeProcessHandle}/${nextBridgeServerRequestOrdinal}`;
714
+ nextBridgeServerRequestOrdinal += 1;
715
+ bridgeServerRequests.set(outerRequestId, value.id);
716
+ manager.onRequest({ ...value, id: outerRequestId });
717
+ return;
718
+ }
719
+ if (value.id === void 0) {
720
+ failBridge("received an app-server response without an id");
721
+ return;
722
+ }
723
+ const request = bridgeRequests.get(value.id);
724
+ bridgeRequests.delete(value.id);
725
+ if ("error" in value) {
726
+ bridge.onError(value.id, value.error, value.metrics);
727
+ return;
728
+ }
729
+ observeBridgeResult(request, value.result);
730
+ bridge.onResult(value.id, value.result, value.metrics);
731
+ };
732
+ const consumeBridgeOutput = (stream, base64) => {
733
+ const binary = atob(base64);
734
+ const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
735
+ outputBuffers[stream] += outputDecoders[stream].decode(bytes, { stream: true });
736
+ if (stream === "stderr") {
737
+ outputBuffers.stderr = outputBuffers.stderr.slice(-4e3);
738
+ return;
739
+ }
740
+ while (true) {
741
+ const newline = outputBuffers.stdout.indexOf("\n");
742
+ if (newline < 0) break;
743
+ const line = outputBuffers.stdout.slice(0, newline).trim();
744
+ outputBuffers.stdout = outputBuffers.stdout.slice(newline + 1);
745
+ if (!line) continue;
746
+ try {
747
+ handleBridgeFrame(JSON.parse(line));
748
+ } catch (error) {
749
+ failBridge(
750
+ `emitted invalid JSON: ${error instanceof Error ? error.message : String(error)}`
751
+ );
752
+ return;
753
+ }
754
+ }
755
+ };
756
+ const handleOuterNotification = (method, parameters) => {
757
+ if (method === "process/exited" && isRecord5(parameters) && parameters.processHandle === bridgeProcessHandle) {
758
+ const exitCode = typeof parameters.exitCode === "number" ? parameters.exitCode : "unknown";
759
+ const stderr = typeof parameters.stderr === "string" ? parameters.stderr.trim() : "";
760
+ failBridge(
761
+ `process exited unexpectedly with code ${exitCode}${stderr ? `: ${stderr}` : ""}`,
762
+ false
763
+ );
764
+ return true;
765
+ }
766
+ if (method !== "process/outputDelta" || !isRecord5(parameters) || parameters.processHandle !== bridgeProcessHandle || parameters.stream !== "stdout" && parameters.stream !== "stderr" || typeof parameters.deltaBase64 !== "string") {
767
+ return false;
768
+ }
769
+ if (parameters.capReached === true) {
770
+ failBridge(`${parameters.stream} was truncated`);
771
+ return true;
772
+ }
773
+ consumeBridgeOutput(parameters.stream, parameters.deltaBase64);
774
+ return true;
775
+ };
776
+ const startBridge = () => {
777
+ if (!isRemoteControlHost) return Promise.resolve();
778
+ if (bridgeReadyPromise) return bridgeReadyPromise;
779
+ bridgeState = "starting";
780
+ bridgeReadyPromise = new Promise((resolve, reject) => {
781
+ bridgeReadyResolve = resolve;
782
+ bridgeReadyReject = reject;
783
+ });
784
+ bridgeReadyTimeout = globalThis.setTimeout(
785
+ () => failBridge("startup timed out after 15000ms"),
786
+ 15e3
787
+ );
788
+ const powershellScript = "$ErrorActionPreference = 'Stop'; $descriptorPath = Join-Path $env:LOCALAPPDATA 'codexhost\\remote-control-bridge-v1.json'; $descriptor = Get-Content -LiteralPath $descriptorPath -Raw | ConvertFrom-Json; if ($descriptor.schemaVersion -ne 1) { throw 'Unsupported codexhost Remote Control descriptor' }; $nodePath = [string]$descriptor.nodePath; $runtimePath = [string]$descriptor.runtimePath; if (-not [System.IO.Path]::IsPathRooted($nodePath) -or -not [System.IO.Path]::IsPathRooted($runtimePath)) { throw 'Invalid codexhost Remote Control runtime path' }; $owner = Get-Process -Id ([int]$descriptor.ownerPid) -ErrorAction SilentlyContinue; if ($null -eq $owner) { throw 'CodexHost Remote Control runtime is not running' }; $env:CODEXHOST_REMOTE_CONTROL_BRIDGE_PIPE = [string]$descriptor.pipePath; & $nodePath $runtimePath '--codexhost-remote-control-bridge'; exit $LASTEXITCODE";
789
+ const command = [
790
+ "powershell.exe",
791
+ "-NoLogo",
792
+ "-NoProfile",
793
+ "-NonInteractive",
794
+ "-EncodedCommand",
795
+ utf16LeBase64(powershellScript)
796
+ ];
797
+ const startedProcessHandle = bridgeProcessHandle;
798
+ try {
799
+ const started = originalSend.call(bridge, "process/spawn", {
800
+ command,
801
+ processHandle: startedProcessHandle,
802
+ cwd: "C:\\",
803
+ streamStdin: true,
804
+ streamStdoutStderr: true,
805
+ outputBytesCap: null,
806
+ timeoutMs: null
807
+ });
808
+ void Promise.resolve(started).catch((error) => {
809
+ if (bridgeProcessHandle === startedProcessHandle) failBridge(error, false);
810
+ });
811
+ } catch (error) {
812
+ failBridge(error);
813
+ }
814
+ return bridgeReadyPromise;
815
+ };
816
+ const writeBridgeFrame = (value) => {
817
+ const operation = async () => {
818
+ await startBridge();
819
+ if (bridgeState !== "ready") throw transportError("is not ready");
820
+ await originalSend.call(bridge, "process/writeStdin", {
821
+ processHandle: bridgeProcessHandle,
822
+ deltaBase64: utf8Base64(`${JSON.stringify(value)}
823
+ `)
824
+ });
825
+ };
826
+ const next = writeTail.then(operation, operation);
827
+ writeTail = next.catch(() => void 0);
828
+ return next;
829
+ };
830
+ const enqueueBridgeRequest = (method, parameters, options) => bridge.enqueueRequest(method, parameters, options, (request) => {
831
+ bridgeRequests.set(request.id, { method, parameters });
832
+ void writeBridgeFrame(request).catch((error) => failBridge(error));
833
+ });
834
+ const initializeBridgeProtocol = () => {
835
+ const initialization = enqueueBridgeRequest("initialize", {
836
+ clientInfo: {
837
+ name: "codexhost_remote_control_bridge",
838
+ title: "codexhost Remote Control bridge",
839
+ version: "1"
840
+ },
841
+ capabilities: {
842
+ experimentalApi: true,
843
+ mcpServerOpenaiFormElicitation: true
844
+ }
845
+ });
846
+ return new Promise((resolve, reject) => {
847
+ const timeout = globalThis.setTimeout(() => {
848
+ const message = "initialization timed out after 15000ms";
849
+ failBridge(message);
850
+ reject(transportError(message));
851
+ }, 15e3);
852
+ void Promise.resolve(initialization).then(
853
+ (value) => {
854
+ globalThis.clearTimeout(timeout);
855
+ resolve(value);
856
+ },
857
+ (error) => {
858
+ globalThis.clearTimeout(timeout);
859
+ reject(error);
860
+ }
861
+ );
862
+ });
863
+ };
864
+ const initializeBridge = () => {
865
+ resetFailedBridge();
866
+ if (bridgeInitialization) return bridgeInitialization;
867
+ bridgeInitialization = startBridge().then(() => initializeBridgeProtocol()).then(() => writeBridgeFrame({ method: "initialized", params: {} }));
868
+ return bridgeInitialization;
869
+ };
870
+ const threadIdFromParameters = (parameters) => {
871
+ if (!isRecord5(parameters)) return null;
872
+ const value = parameters.threadId ?? parameters.conversationId;
873
+ return typeof value === "string" ? value : null;
874
+ };
875
+ const isThreadScopedMethod = (method) => method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("review/");
876
+ const resolveThreadOwnership = (threadId) => {
877
+ if (knownExternalThreadIds.has(threadId)) return Promise.resolve("external");
878
+ if (knownOfficialThreadIds.has(threadId)) return Promise.resolve("codex");
879
+ const pending = threadOwnershipResolutions.get(threadId);
880
+ if (pending) return pending;
881
+ const resolution = initializeBridge().then(
882
+ () => enqueueBridgeRequest("codexhost/thread/ownership/list", {
883
+ threadIds: [threadId]
884
+ })
885
+ ).then((value) => {
886
+ const ownerships = isRecord5(value) && Array.isArray(value.threads) ? value.threads : null;
887
+ const ownership = ownerships?.[0];
888
+ if (!ownerships || !isRecord5(ownership) || ownerships.length !== 1 || ownership.threadId !== threadId || ownership.owner !== "external" && ownership.owner !== "codex") {
889
+ throw transportError("Thread ownership lookup returned an invalid result");
890
+ }
891
+ if (ownership.owner === "external") {
892
+ knownExternalThreadIds.add(threadId);
893
+ knownOfficialThreadIds.delete(threadId);
894
+ return "external";
895
+ }
896
+ knownOfficialThreadIds.add(threadId);
897
+ knownExternalThreadIds.delete(threadId);
898
+ return "codex";
899
+ });
900
+ threadOwnershipResolutions.set(threadId, resolution);
901
+ const clearResolution = () => {
902
+ if (threadOwnershipResolutions.get(threadId) === resolution) {
903
+ threadOwnershipResolutions.delete(threadId);
904
+ }
905
+ };
906
+ void resolution.then(clearResolution, clearResolution);
907
+ return resolution;
908
+ };
909
+ const shouldResolveThreadOwnership = (method, parameters) => {
910
+ if (!isRemoteControlHost || method.startsWith("codexhost/") || !isThreadScopedMethod(method)) {
911
+ return null;
912
+ }
913
+ const threadId = threadIdFromParameters(parameters);
914
+ if (!threadId || knownExternalThreadIds.has(threadId) || knownOfficialThreadIds.has(threadId)) {
915
+ return null;
916
+ }
917
+ return threadId;
918
+ };
919
+ const shouldUseBridge = (method, parameters) => {
920
+ if (!isRemoteControlHost) return false;
921
+ if (method.startsWith("codexhost/")) return true;
922
+ if (method === "thread/list") return true;
923
+ if (method === "thread/start") {
924
+ return isRecord5(parameters) && typeof parameters.model === "string" && parameters.model.startsWith("codexhost/");
925
+ }
926
+ const threadId = threadIdFromParameters(parameters);
927
+ if (threadId && knownExternalThreadIds.has(threadId)) return true;
928
+ if (threadId && knownOfficialThreadIds.has(threadId)) return false;
929
+ return selectedModel !== null && (method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("review/"));
930
+ };
571
931
  const routeThreadStart = (parameters) => {
572
932
  if (selectedModel === null || !isRecord5(parameters) || parameters.ephemeral === true) {
573
933
  return parameters;
@@ -576,19 +936,62 @@ function installDraftPrewarmPolicyBridge(bridge, hostId, target, prewarmedThread
576
936
  };
577
937
  const routedSend = (method, parameters, options) => {
578
938
  const routedParameters = method === "thread/start" ? routeThreadStart(parameters) : parameters;
579
- return options === void 0 ? originalSend.call(bridge, method, routedParameters) : originalSend.call(bridge, method, routedParameters, options);
939
+ const sendBridged = () => initializeBridge().then(
940
+ () => enqueueBridgeRequest(method, routedParameters, options)
941
+ );
942
+ const sendDirect = () => options === void 0 ? originalSend.call(bridge, method, routedParameters) : originalSend.call(bridge, method, routedParameters, options);
943
+ const unresolvedThreadId = shouldResolveThreadOwnership(method, routedParameters);
944
+ if (unresolvedThreadId) {
945
+ return resolveThreadOwnership(unresolvedThreadId).then(
946
+ (owner) => owner === "external" ? sendBridged() : sendDirect()
947
+ );
948
+ }
949
+ return shouldUseBridge(method, routedParameters) ? sendBridged() : sendDirect();
580
950
  };
581
951
  const routedPrewarm = (parameters, options) => {
582
952
  const routedParameters = routeThreadStart(parameters);
953
+ if (shouldUseBridge("thread/start", routedParameters)) {
954
+ return routedSend("thread/start", routedParameters, options);
955
+ }
583
956
  return options === void 0 ? originalPrewarm.call(bridge, routedParameters) : originalPrewarm.call(bridge, routedParameters, options);
584
957
  };
585
958
  bridge.sendRequest = routedSend;
586
959
  bridge.prewarmThreadStart = routedPrewarm;
960
+ const routedWindowMessage = (event) => {
961
+ const message = event.data;
962
+ if (!isRecord5(message) || message.type !== "mcp-notification" || message.hostId !== hostId || typeof message.method !== "string") {
963
+ return;
964
+ }
965
+ handleOuterNotification(message.method, message.params);
966
+ };
967
+ const observesWindowNotifications = isRemoteControlHost && typeof target.addEventListener === "function" && typeof target.removeEventListener === "function";
968
+ if (observesWindowNotifications) target.addEventListener?.("message", routedWindowMessage);
969
+ const routedOnNotification = (method, parameters) => {
970
+ if (!handleOuterNotification(method, parameters)) {
971
+ originalOnNotification.call(manager, method, parameters);
972
+ }
973
+ };
974
+ const routedDispatchAppServerResponse = (method, response) => {
975
+ if (isRemoteControlHost && typeof response.id === "string") {
976
+ const innerRequestId = bridgeServerRequests.get(response.id);
977
+ if (innerRequestId !== void 0 || bridgeServerRequests.has(response.id)) {
978
+ bridgeServerRequests.delete(response.id);
979
+ void writeBridgeFrame({ ...response, id: innerRequestId }).catch(
980
+ (error) => failBridge(error)
981
+ );
982
+ return void 0;
983
+ }
984
+ if (response.id.startsWith(bridgeServerRequestIdPrefix)) return void 0;
985
+ }
986
+ return originalDispatchAppServerResponse.call(manager, method, response);
987
+ };
988
+ if (!observesWindowNotifications) manager.onNotification = routedOnNotification;
989
+ manager.dispatchAppServerResponse = routedDispatchAppServerResponse;
587
990
  const policy = Object.freeze({
588
991
  state: "ready",
589
992
  hostId,
590
- owns(candidate, candidateHostId, candidatePrewarmedThreadManager) {
591
- return candidate === bridge && candidateHostId === hostId && candidatePrewarmedThreadManager === prewarmedThreadManager;
993
+ owns(candidateManager, candidate, candidateHostId, candidatePrewarmedThreadManager) {
994
+ return candidateManager === manager && candidate === bridge && candidateHostId === hostId && candidatePrewarmedThreadManager === prewarmedThreadManager;
592
995
  },
593
996
  select(model) {
594
997
  if (model !== null && (typeof model !== "string" || !model.startsWith("codexhost/"))) {
@@ -607,6 +1010,30 @@ function installDraftPrewarmPolicyBridge(bridge, hostId, target, prewarmedThread
607
1010
  if (bridge.prewarmThreadStart === routedPrewarm) {
608
1011
  bridge.prewarmThreadStart = originalPrewarm;
609
1012
  }
1013
+ if (observesWindowNotifications) {
1014
+ target.removeEventListener?.("message", routedWindowMessage);
1015
+ } else if (manager.onNotification === routedOnNotification) {
1016
+ manager.onNotification = originalOnNotification;
1017
+ }
1018
+ if (manager.dispatchAppServerResponse === routedDispatchAppServerResponse) {
1019
+ manager.dispatchAppServerResponse = originalDispatchAppServerResponse;
1020
+ }
1021
+ if (bridgeReadyTimeout !== null) globalThis.clearTimeout(bridgeReadyTimeout);
1022
+ bridgeReadyTimeout = null;
1023
+ if (isRemoteControlHost && (bridgeState === "starting" || bridgeState === "ready")) {
1024
+ void Promise.resolve(
1025
+ originalSend.call(bridge, "process/kill", { processHandle: bridgeProcessHandle })
1026
+ ).catch(() => void 0);
1027
+ }
1028
+ bridgeState = "disposed";
1029
+ const disposedError = transportError("was disposed");
1030
+ bridgeReadyReject?.(disposedError);
1031
+ for (const requestId of bridgeRequests.keys()) bridge.onError(requestId, disposedError);
1032
+ bridgeRequests.clear();
1033
+ bridgeServerRequests.clear();
1034
+ knownExternalThreadIds.clear();
1035
+ knownOfficialThreadIds.clear();
1036
+ threadOwnershipResolutions.clear();
610
1037
  selectedModel = null;
611
1038
  }
612
1039
  });
@@ -648,10 +1075,13 @@ async function installDraftPrewarmPolicyInRenderer(contents, findRequestManagerE
648
1075
  const manager = managerProperties.result?.find(
649
1076
  (property) => property.name === "manager"
650
1077
  )?.value;
1078
+ const requestClient = managerProperties.result?.find(
1079
+ (property) => property.name === "requestClient"
1080
+ )?.value;
651
1081
  const prewarmedThreadManager = managerProperties.result?.find(
652
1082
  (property) => property.name === "prewarmedThreadManager"
653
1083
  )?.value;
654
- if (candidateCount !== 1 || typeof hostId !== "string" || hostId.length === 0 || typeof manager?.objectId !== "string") {
1084
+ if (candidateCount !== 1 || typeof hostId !== "string" || hostId.length === 0 || typeof manager?.objectId !== "string" || typeof requestClient?.objectId !== "string") {
655
1085
  throw new Error("Renderer request manager is ambiguous");
656
1086
  }
657
1087
  if (typeof prewarmedThreadManager?.objectId !== "string") {
@@ -660,7 +1090,11 @@ async function installDraftPrewarmPolicyInRenderer(contents, findRequestManagerE
660
1090
  const installed = await contents.debugger.sendCommand("Runtime.callFunctionOn", {
661
1091
  objectId: manager.objectId,
662
1092
  functionDeclaration: installRendererPolicyFunction,
663
- arguments: [{ value: hostId }, { objectId: prewarmedThreadManager.objectId }],
1093
+ arguments: [
1094
+ { objectId: requestClient.objectId },
1095
+ { value: hostId },
1096
+ { objectId: prewarmedThreadManager.objectId }
1097
+ ],
664
1098
  awaitPromise: true,
665
1099
  returnByValue: true
666
1100
  });
@@ -748,12 +1182,19 @@ var FIND_REQUEST_MANAGER_EXPRESSION = `(() => {
748
1182
  return {
749
1183
  candidateCount: selected == null ? candidates.length : 1,
750
1184
  hostId: selected?.hostId ?? null,
751
- manager: selected?.requestClient ?? null,
1185
+ manager: selected?.manager ?? null,
1186
+ requestClient: selected?.requestClient ?? null,
752
1187
  prewarmedThreadManager: selected?.prewarmedThreadManager ?? null,
753
1188
  };
754
1189
  })()`;
755
- var INSTALL_RENDERER_POLICY_FUNCTION = `function(hostId, prewarmedThreadManager) {
756
- return (${installDraftPrewarmPolicyBridge.toString()})(this, hostId, window, prewarmedThreadManager);
1190
+ var INSTALL_RENDERER_POLICY_FUNCTION = `function(requestClient, hostId, prewarmedThreadManager) {
1191
+ return (${installDraftPrewarmPolicyBridge.toString()})(
1192
+ this,
1193
+ requestClient,
1194
+ hostId,
1195
+ window,
1196
+ prewarmedThreadManager,
1197
+ );
757
1198
  }`;
758
1199
  var REQUEST_MANAGER_WAIT_TIMEOUT_MS = 6e4;
759
1200
  var REQUEST_MANAGER_POLL_INTERVAL_MS = 25;
@@ -826,12 +1267,16 @@ function validateBindingStatus(value, expectedAgents) {
826
1267
  }
827
1268
  return value;
828
1269
  }
829
- function selectRendererWebContents(contents) {
1270
+ function selectRendererWebContents(contents, preferredRendererId) {
830
1271
  const candidates = contents.filter(
831
1272
  (item) => item.type === "window" && item.surface === "primary" && item.runtime.available && item.runtime.elementCount !== null
832
1273
  ).toSorted(
833
1274
  (left, right) => (right.runtime.elementCount ?? 0) - (left.runtime.elementCount ?? 0)
834
1275
  );
1276
+ const preferred = candidates.find(
1277
+ (candidate) => candidate.id === preferredRendererId && (candidate.runtime.elementCount ?? 0) > 0
1278
+ );
1279
+ if (preferred) return preferred;
835
1280
  return candidates.find((candidate) => (candidate.runtime.elementCount ?? 0) > 0) ?? null;
836
1281
  }
837
1282
  async function waitForRendererTitlePolicyReady(markReadiness, options = {}) {
@@ -959,13 +1404,13 @@ var defaultOperations = {
959
1404
  ),
960
1405
  readTitlePolicyCounters: readMainProcessTitlePolicyCounters
961
1406
  };
962
- async function waitForRenderer(inspector, operations, timeoutMs, pollIntervalMs) {
1407
+ async function waitForRenderer(inspector, operations, timeoutMs, pollIntervalMs, preferredRendererId) {
963
1408
  const deadline = Date.now() + timeoutMs;
964
1409
  let candidateCount = 0;
965
1410
  while (Date.now() < deadline) {
966
1411
  const inventory = await operations.inspect(inspector);
967
1412
  candidateCount = inventory.length;
968
- const renderer = selectRendererWebContents(inventory);
1413
+ const renderer = selectRendererWebContents(inventory, preferredRendererId);
969
1414
  if (renderer) return renderer;
970
1415
  await sleep(pollIntervalMs);
971
1416
  }
@@ -1016,7 +1461,8 @@ var InstalledRendererControlSession = class {
1016
1461
  this.inspector,
1017
1462
  this.operations,
1018
1463
  this.timeoutMs,
1019
- this.pollIntervalMs
1464
+ this.pollIntervalMs,
1465
+ this.#snapshot.renderer.id
1020
1466
  );
1021
1467
  const existing = await this.operations.readBinding(this.inspector, selected.id).catch(() => null);
1022
1468
  if (existing !== null) {
@@ -1124,7 +1570,13 @@ async function createRendererControlSession(options) {
1124
1570
  startupTrace("reloading Renderer");
1125
1571
  await operations.reload(options.inspector, initial.id);
1126
1572
  startupTrace("waiting for reloaded Renderer");
1127
- const selected = await waitForRenderer(options.inspector, operations, timeoutMs, pollIntervalMs);
1573
+ const selected = await waitForRenderer(
1574
+ options.inspector,
1575
+ operations,
1576
+ timeoutMs,
1577
+ pollIntervalMs,
1578
+ initial.id
1579
+ );
1128
1580
  startupTrace("waiting for title policy readiness");
1129
1581
  const titlePolicyReadiness = await waitForRendererTitlePolicyReady(
1130
1582
  () => operations.markTitlePolicyReady(options.inspector, selected.id),