@codexhost/cli-linux-x64 0.2.6 → 0.3.0-test.2

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.
@@ -36,6 +36,7 @@
36
36
  #ownershipRequestGenerations = /* @__PURE__ */ new WeakMap();
37
37
  #states = /* @__PURE__ */ new WeakMap();
38
38
  #switching = /* @__PURE__ */ new Set();
39
+ #pendingSubmissions = /* @__PURE__ */ new Set();
39
40
  #composerSequence = 0;
40
41
  #modelRequestSequence = 0;
41
42
  #ownershipRequestSequence = 0;
@@ -95,6 +96,7 @@
95
96
  rebindConversation(composer, target) {
96
97
  if (!isConversationTarget(target)) return null;
97
98
  const previous = this.#state(composer);
99
+ this.#pendingSubmissions.delete(previous);
98
100
  this.#modelRequestGenerations.set(previous, ++this.#modelRequestSequence);
99
101
  this.#ownershipRequestGenerations.set(previous, ++this.#ownershipRequestSequence);
100
102
  let state = this.#conversationState(target);
@@ -114,6 +116,7 @@
114
116
  restore(composer, agent, model, thinkingOptionId, permissionModeId) {
115
117
  if (!this.#enabledAgents.has(agent)) return null;
116
118
  const state = this.#state(composer);
119
+ this.#pendingSubmissions.delete(state);
117
120
  state.agent = agent;
118
121
  state.phase = "locked";
119
122
  if (agent === "pi" && model) state.piModel = model;
@@ -204,9 +207,21 @@
204
207
  }
205
208
  lock(composer) {
206
209
  const state = this.#state(composer);
210
+ this.#pendingSubmissions.delete(state);
207
211
  state.phase = "locked";
208
212
  return state;
209
213
  }
214
+ markSubmissionPending(composer) {
215
+ const state = this.#state(composer);
216
+ if (state.phase === "draft") this.#pendingSubmissions.add(state);
217
+ return state;
218
+ }
219
+ isSubmissionPending(composer) {
220
+ return this.#pendingSubmissions.has(this.#state(composer));
221
+ }
222
+ clearPendingSubmission(composer) {
223
+ this.#pendingSubmissions.delete(this.#state(composer));
224
+ }
210
225
  recordSubmission(composer) {
211
226
  const state = this.#state(composer);
212
227
  this.#lastSubmittedAgent = state.agent;
@@ -224,6 +239,9 @@
224
239
  if (isConversationTarget(target) && !bound) {
225
240
  this.#conversationStates.push({ target, state });
226
241
  }
242
+ if (isConversationTarget(target) && this.#pendingSubmissions.delete(state)) {
243
+ state.phase = "locked";
244
+ }
227
245
  return true;
228
246
  }
229
247
  async switchAgent(composer, nextAgent, operations) {
@@ -231,6 +249,7 @@
231
249
  if (!this.#enabledAgents.has(nextAgent)) return false;
232
250
  if (state.phase !== "draft" || this.#switching.has(state)) return false;
233
251
  if (state.agent === nextAgent) return true;
252
+ this.#pendingSubmissions.delete(state);
234
253
  this.#switching.add(state);
235
254
  try {
236
255
  if (!operations.applyAgent(nextAgent)) return false;
@@ -15783,21 +15802,88 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15783
15802
  return view;
15784
15803
  }
15785
15804
 
15805
+ // src/renderer-model-picker-positioning.ts
15806
+ var MENU_GAP = 4;
15807
+ var MAIN_MENU_SIDE_OFFSET = 8;
15808
+ var COLLISION_PADDING = 8;
15809
+ var RENDERER_MODEL_PICKER_MAIN_MENU_WIDTH = 260;
15810
+ var RENDERER_MODEL_PICKER_MODEL_MENU_WIDTH = 280;
15811
+ var RENDERER_MODEL_PICKER_MODEL_MENU_MAX_HEIGHT = 360;
15812
+ function clampPosition(value, minimum, maximum) {
15813
+ return Math.min(Math.max(value, minimum), Math.max(minimum, maximum));
15814
+ }
15815
+ function fitWidth(preferredWidth, viewportWidth) {
15816
+ return Math.max(
15817
+ COLLISION_PADDING,
15818
+ Math.min(preferredWidth, viewportWidth - COLLISION_PADDING * 2)
15819
+ );
15820
+ }
15821
+ function fitHeight(viewport, top = COLLISION_PADDING) {
15822
+ return Math.max(
15823
+ COLLISION_PADDING,
15824
+ Math.min(
15825
+ RENDERER_MODEL_PICKER_MODEL_MENU_MAX_HEIGHT,
15826
+ viewport.height * 0.6,
15827
+ viewport.height - top - COLLISION_PADDING
15828
+ )
15829
+ );
15830
+ }
15831
+ function rendererModelPickerMainMenuPlacement(triggerRect, viewport, width = RENDERER_MODEL_PICKER_MAIN_MENU_WIDTH) {
15832
+ const maxLeft = viewport.width - COLLISION_PADDING - width;
15833
+ return {
15834
+ left: clampPosition(triggerRect.right - width, COLLISION_PADDING, maxLeft),
15835
+ width,
15836
+ bottom: Math.max(COLLISION_PADDING, viewport.height - triggerRect.top + MAIN_MENU_SIDE_OFFSET)
15837
+ };
15838
+ }
15839
+ function rendererModelPickerStandaloneModelMenuPlacement(triggerRect, viewport) {
15840
+ const width = fitWidth(RENDERER_MODEL_PICKER_MODEL_MENU_WIDTH, viewport.width);
15841
+ const maxLeft = viewport.width - COLLISION_PADDING - width;
15842
+ return {
15843
+ left: clampPosition(triggerRect.right - width, COLLISION_PADDING, maxLeft),
15844
+ width,
15845
+ maxHeight: fitHeight(viewport),
15846
+ bottom: Math.max(COLLISION_PADDING, viewport.height - triggerRect.top + MAIN_MENU_SIDE_OFFSET)
15847
+ };
15848
+ }
15849
+ function rendererModelPickerModelMenuPlacement(mainRect, viewport) {
15850
+ const preferredWidth = fitWidth(RENDERER_MODEL_PICKER_MODEL_MENU_WIDTH, viewport.width);
15851
+ const rightLeft = mainRect.right + MENU_GAP;
15852
+ const leftLeft = mainRect.left - MENU_GAP - preferredWidth;
15853
+ const rightAvailable = viewport.width - rightLeft - COLLISION_PADDING;
15854
+ const leftAvailable = mainRect.left - MENU_GAP - COLLISION_PADDING;
15855
+ let width;
15856
+ let left;
15857
+ if (rightAvailable >= preferredWidth) {
15858
+ width = preferredWidth;
15859
+ left = rightLeft;
15860
+ } else if (leftAvailable >= preferredWidth) {
15861
+ width = preferredWidth;
15862
+ left = leftLeft;
15863
+ } else if (rightAvailable >= leftAvailable) {
15864
+ width = Math.max(COLLISION_PADDING, rightAvailable);
15865
+ left = rightLeft;
15866
+ } else {
15867
+ width = Math.max(COLLISION_PADDING, leftAvailable);
15868
+ left = mainRect.left - MENU_GAP - width;
15869
+ }
15870
+ const top = clampPosition(mainRect.top, COLLISION_PADDING, viewport.height - COLLISION_PADDING);
15871
+ const maxHeight = fitHeight(viewport, top);
15872
+ return {
15873
+ left: clampPosition(left, COLLISION_PADDING, viewport.width - COLLISION_PADDING - width),
15874
+ top,
15875
+ width,
15876
+ maxHeight
15877
+ };
15878
+ }
15879
+
15786
15880
  // src/renderer-model-picker.ts
15787
15881
  var RENDERER_MODEL_TRIGGER_FALLBACK_CLASSES = "border-token-border no-drag cursor-interaction items-center gap-1 border whitespace-nowrap select-none focus:outline-none disabled:cursor-not-allowed disabled:opacity-40 flex rounded-full text-token-text-tertiary enabled:hover:bg-token-list-hover-background enabled:active:bg-token-foreground/15 data-[state=open]:bg-token-list-hover-background border-transparent h-token-button-composer px-2 py-0 text-sm leading-[18px] min-w-0";
15788
15882
  var MENU_CLASSES = "fixed z-50 overflow-hidden rounded-xl bg-token-dropdown-background/90 text-token-foreground shadow-lg backdrop-blur-xl";
15789
15883
  var SEARCH_INPUT_CLASSES = "mb-1 w-full shrink-0 rounded-lg border border-token-border bg-token-dropdown-background/95 px-2 py-1.5 text-sm text-token-foreground outline-none placeholder:text-token-text-tertiary disabled:cursor-not-allowed disabled:opacity-40";
15790
15884
  var OPTION_CLASSES = "flex w-full cursor-interaction items-center gap-2 rounded-lg px-2 py-2 text-left text-sm text-token-foreground outline-none enabled:hover:bg-token-list-hover-background enabled:active:bg-token-foreground/15 disabled:cursor-not-allowed disabled:opacity-40";
15791
15885
  var HEADING_CLASSES = "px-2 pb-1 pt-1.5 text-sm text-token-text-tertiary";
15792
- var MAIN_MENU_MIN_WIDTH = 160;
15793
- var MAIN_MENU_MAX_WIDTH = 180;
15794
- var MODEL_MENU_PREFERRED_WIDTH = 360;
15795
- var MODEL_MENU_MAX_WIDTH = 420;
15796
- var MODEL_MENU_MAX_HEIGHT = 360;
15797
15886
  var MODEL_TRIGGER_MAX_WIDTH = "min(200px, 26vw)";
15798
- var MAIN_MENU_LEFT_OFFSET = 96;
15799
- var MENU_GAP = 4;
15800
- var VIEWPORT_MARGIN = 8;
15801
15887
  var MODEL_SCROLLBAR_STYLE_ATTRIBUTE = "data-codexhost-model-picker-scrollbar";
15802
15888
  function popoverOpen2(menu) {
15803
15889
  return menu.matches(":popover-open");
@@ -15877,47 +15963,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15877
15963
  }
15878
15964
  function positionMainMenu(control) {
15879
15965
  const triggerRect = control.trigger.getBoundingClientRect();
15880
- const width = Math.min(MAIN_MENU_MAX_WIDTH, Math.max(MAIN_MENU_MIN_WIDTH, triggerRect.width));
15881
- const modelMenuWidth = Math.min(
15882
- MODEL_MENU_MAX_WIDTH,
15883
- Math.min(MODEL_MENU_PREFERRED_WIDTH, window.innerWidth - VIEWPORT_MARGIN * 2)
15966
+ const placement = rendererModelPickerMainMenuPlacement(
15967
+ triggerRect,
15968
+ { width: window.innerWidth, height: window.innerHeight },
15969
+ RENDERER_MODEL_PICKER_MAIN_MENU_WIDTH
15884
15970
  );
15885
- const preferredLeft = triggerRect.right - width - MAIN_MENU_LEFT_OFFSET;
15886
- const rightReservedLeft = window.innerWidth - VIEWPORT_MARGIN - modelMenuWidth - MENU_GAP - width;
15887
- const maxLeft = window.innerWidth - VIEWPORT_MARGIN - width;
15888
- const left = Math.max(
15889
- VIEWPORT_MARGIN,
15890
- Math.min(preferredLeft, Math.max(rightReservedLeft, VIEWPORT_MARGIN), maxLeft)
15891
- );
15892
- control.menu.style.setProperty("width", `${width}px`, "important");
15893
- control.menu.style.left = `${left}px`;
15894
- control.menu.style.maxWidth = `${width}px`;
15971
+ control.menu.style.setProperty("width", `${placement.width}px`, "important");
15972
+ control.menu.style.left = `${placement.left}px`;
15973
+ control.menu.style.maxWidth = `${placement.width}px`;
15895
15974
  control.menu.style.right = "auto";
15896
15975
  control.menu.style.top = "auto";
15897
- control.menu.style.bottom = `${Math.max(
15898
- VIEWPORT_MARGIN,
15899
- window.innerHeight - triggerRect.top + 6
15900
- )}px`;
15901
- }
15902
- function positionModelMenu(control) {
15903
- const mainRect = control.menu.getBoundingClientRect();
15904
- const availableRight = window.innerWidth - mainRect.right - VIEWPORT_MARGIN;
15905
- const width = Math.min(MODEL_MENU_MAX_WIDTH, Math.max(VIEWPORT_MARGIN, availableRight));
15906
- const left = mainRect.right + MENU_GAP;
15907
- control.modelMenu.style.setProperty("width", `${width}px`, "important");
15908
- control.modelMenu.style.left = `${left}px`;
15909
- control.modelMenu.style.maxWidth = `${width}px`;
15910
- const maxHeight = Math.max(
15911
- VIEWPORT_MARGIN,
15912
- Math.min(MODEL_MENU_MAX_HEIGHT, window.innerHeight * 0.6, mainRect.bottom - VIEWPORT_MARGIN)
15913
- );
15914
- control.modelMenu.style.maxHeight = `${maxHeight}px`;
15976
+ control.menu.style.bottom = `${placement.bottom}px`;
15977
+ }
15978
+ function positionAdvancedMenus(control) {
15979
+ positionMainMenu(control);
15980
+ positionModelMenu(control);
15981
+ }
15982
+ function positionModelMenu(control, standalone = false) {
15983
+ const anchorRect = standalone ? control.trigger.getBoundingClientRect() : control.menu.getBoundingClientRect();
15984
+ const placement = standalone ? rendererModelPickerStandaloneModelMenuPlacement(anchorRect, {
15985
+ width: window.innerWidth,
15986
+ height: window.innerHeight
15987
+ }) : rendererModelPickerModelMenuPlacement(anchorRect, {
15988
+ width: window.innerWidth,
15989
+ height: window.innerHeight
15990
+ });
15991
+ control.modelMenu.style.setProperty("width", `${placement.width}px`, "important");
15992
+ control.modelMenu.style.left = `${placement.left}px`;
15993
+ control.modelMenu.style.maxWidth = `${placement.width}px`;
15994
+ control.modelMenu.style.maxHeight = `${placement.maxHeight}px`;
15915
15995
  control.modelMenu.style.right = "auto";
15916
- control.modelMenu.style.top = "auto";
15917
- control.modelMenu.style.bottom = `${Math.max(
15918
- VIEWPORT_MARGIN,
15919
- window.innerHeight - mainRect.bottom
15920
- )}px`;
15996
+ control.modelMenu.style.top = placement.top === void 0 ? "auto" : `${placement.top}px`;
15997
+ control.modelMenu.style.bottom = placement.bottom === void 0 ? "auto" : `${placement.bottom}px`;
15921
15998
  }
15922
15999
  function syncRendererModelTriggerClass(control, nativeClassName) {
15923
16000
  control.trigger.className = nativeClassName?.trim() || RENDERER_MODEL_TRIGGER_FALLBACK_CLASSES;
@@ -16012,7 +16089,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16012
16089
  modelMenu.style.margin = "0";
16013
16090
  modelMenu.style.padding = "4px";
16014
16091
  modelMenu.style.border = "0";
16015
- modelMenu.style.maxHeight = "min(360px, 60vh)";
16092
+ modelMenu.style.maxHeight = `min(${RENDERER_MODEL_PICKER_MODEL_MENU_MAX_HEIGHT}px, 60vh)`;
16016
16093
  modelMenu.style.overflowY = "auto";
16017
16094
  modelButton.setAttribute("aria-controls", modelMenu.id);
16018
16095
  const options = /* @__PURE__ */ new Map();
@@ -16073,33 +16150,41 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16073
16150
  applyModelSearchFilter(control);
16074
16151
  }
16075
16152
  };
16153
+ const pickerOpen = () => popoverOpen2(menu) || popoverOpen2(modelMenu);
16076
16154
  const close = () => {
16077
16155
  closeModelMenu();
16078
16156
  if (popoverOpen2(menu)) menu.hidePopover();
16079
16157
  };
16080
- const openModelMenu = () => {
16081
- if (!popoverOpen2(menu) || popoverOpen2(modelMenu)) return;
16158
+ const openModelMenu = (standalone = false) => {
16159
+ if (!standalone && !popoverOpen2(menu) || popoverOpen2(modelMenu)) return;
16082
16160
  modelMenu.showPopover();
16083
- positionModelMenu(control);
16161
+ positionModelMenu(control, standalone);
16084
16162
  modelButton.setAttribute("aria-expanded", "true");
16085
16163
  };
16086
16164
  const open = () => {
16087
- if (trigger.disabled || popoverOpen2(menu)) return;
16165
+ if (trigger.disabled || pickerOpen()) return;
16166
+ if (control.thinkingOptions.size === 0) {
16167
+ openModelMenu(true);
16168
+ return;
16169
+ }
16088
16170
  menu.showPopover();
16089
- positionMainMenu(control);
16171
+ positionAdvancedMenus(control);
16090
16172
  };
16091
16173
  const onTriggerClick = () => {
16092
- if (popoverOpen2(menu)) close();
16174
+ if (pickerOpen()) close();
16093
16175
  else open();
16094
16176
  };
16095
16177
  const onToggle = () => {
16096
16178
  const openState = popoverOpen2(menu);
16097
- trigger.setAttribute("aria-expanded", String(openState));
16179
+ trigger.setAttribute("aria-expanded", String(openState || popoverOpen2(modelMenu)));
16098
16180
  trigger.setAttribute("data-state", openState ? "open" : "closed");
16099
16181
  if (!openState) closeModelMenu();
16100
16182
  };
16101
16183
  const onModelToggle = () => {
16102
- modelButton.setAttribute("aria-expanded", String(popoverOpen2(modelMenu)));
16184
+ const openState = popoverOpen2(modelMenu);
16185
+ modelButton.setAttribute("aria-expanded", String(openState));
16186
+ trigger.setAttribute("aria-expanded", String(openState || popoverOpen2(menu)));
16187
+ trigger.setAttribute("data-state", openState || popoverOpen2(menu) ? "open" : "closed");
16103
16188
  };
16104
16189
  const onRootClick = (event) => {
16105
16190
  const target = event.target instanceof Element ? event.target.closest("button") : null;
@@ -16138,21 +16223,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16138
16223
  trigger.focus();
16139
16224
  };
16140
16225
  const onViewportChange = () => {
16141
- if (popoverOpen2(menu)) positionMainMenu(control);
16142
- if (popoverOpen2(modelMenu)) positionModelMenu(control);
16226
+ if (popoverOpen2(menu)) positionAdvancedMenus(control);
16227
+ else if (popoverOpen2(modelMenu)) positionModelMenu(control, true);
16143
16228
  };
16144
16229
  trigger.addEventListener("click", onTriggerClick);
16145
16230
  menu.addEventListener("toggle", onToggle);
16146
16231
  modelMenu.addEventListener("toggle", onModelToggle);
16147
16232
  modelButton.addEventListener("mouseenter", onModelHover);
16148
- root.addEventListener("click", onRootClick);
16233
+ menu.addEventListener("click", onRootClick);
16149
16234
  modelMenu.addEventListener("click", onModelMenuClick);
16150
16235
  document.addEventListener("pointerdown", onDocumentPointerDown, true);
16151
16236
  document.addEventListener("keydown", onDocumentKeyDown, true);
16152
16237
  window.addEventListener("resize", onViewportChange);
16153
16238
  window.addEventListener("scroll", onViewportChange, true);
16154
- root.append(trigger, menu);
16155
- document.body.append(modelMenu);
16239
+ root.append(trigger);
16240
+ document.body.append(menu, modelMenu);
16156
16241
  searchHeader.append(searchInput);
16157
16242
  modelMenu.append(searchHeader, searchEmpty);
16158
16243
  const control = {
@@ -16175,7 +16260,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16175
16260
  menu.removeEventListener("toggle", onToggle);
16176
16261
  modelMenu.removeEventListener("toggle", onModelToggle);
16177
16262
  modelButton.removeEventListener("mouseenter", onModelHover);
16178
- root.removeEventListener("click", onRootClick);
16263
+ menu.removeEventListener("click", onRootClick);
16179
16264
  modelMenu.removeEventListener("click", onModelMenuClick);
16180
16265
  searchInput.removeEventListener("input", onSearchInput);
16181
16266
  for (const type of silencedEventTypes) {
@@ -16186,6 +16271,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16186
16271
  document.removeEventListener("keydown", onDocumentKeyDown, true);
16187
16272
  window.removeEventListener("resize", onViewportChange);
16188
16273
  window.removeEventListener("scroll", onViewportChange, true);
16274
+ menu.remove();
16189
16275
  modelMenu.remove();
16190
16276
  root.remove();
16191
16277
  }
@@ -17138,7 +17224,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17138
17224
  var CONTROL_ATTRIBUTE2 = "data-codexhost-harness-command-control";
17139
17225
  var MENU_ATTRIBUTE = "data-codexhost-harness-command-menu";
17140
17226
  var MENU_WIDTH = 320;
17141
- var VIEWPORT_MARGIN2 = 8;
17227
+ var VIEWPORT_MARGIN = 8;
17142
17228
  var MENU_GAP2 = 8;
17143
17229
  var SVG_NS = "http://www.w3.org/2000/svg";
17144
17230
  var COMMAND_ICON_PATHS = [
@@ -17254,7 +17340,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17254
17340
  menu.style.inset = "auto";
17255
17341
  menu.style.zIndex = "2147483647";
17256
17342
  menu.style.width = `${MENU_WIDTH}px`;
17257
- menu.style.maxWidth = `calc(100vw - ${VIEWPORT_MARGIN2 * 2}px)`;
17343
+ menu.style.maxWidth = `calc(100vw - ${VIEWPORT_MARGIN * 2}px)`;
17258
17344
  menu.style.maxHeight = "min(360px, calc(100vh - 16px))";
17259
17345
  menu.style.overflowY = "auto";
17260
17346
  menu.style.padding = "4px";
@@ -17275,14 +17361,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17275
17361
  const positionMenu2 = () => {
17276
17362
  const rect = trigger.getBoundingClientRect();
17277
17363
  const menuHeight = menu.getBoundingClientRect().height;
17278
- const opensAbove = rect.top >= menuHeight + MENU_GAP2 + VIEWPORT_MARGIN2;
17364
+ const opensAbove = rect.top >= menuHeight + MENU_GAP2 + VIEWPORT_MARGIN;
17279
17365
  const left = clamp(
17280
17366
  rect.left,
17281
- VIEWPORT_MARGIN2,
17282
- window.innerWidth - MENU_WIDTH - VIEWPORT_MARGIN2
17367
+ VIEWPORT_MARGIN,
17368
+ window.innerWidth - MENU_WIDTH - VIEWPORT_MARGIN
17283
17369
  );
17284
17370
  menu.style.left = `${left}px`;
17285
- menu.style.top = opensAbove ? `${Math.max(VIEWPORT_MARGIN2, rect.top - menuHeight - MENU_GAP2)}px` : `${Math.min(window.innerHeight - menuHeight - VIEWPORT_MARGIN2, rect.bottom + MENU_GAP2)}px`;
17371
+ menu.style.top = opensAbove ? `${Math.max(VIEWPORT_MARGIN, rect.top - menuHeight - MENU_GAP2)}px` : `${Math.min(window.innerHeight - menuHeight - VIEWPORT_MARGIN, rect.bottom + MENU_GAP2)}px`;
17286
17372
  };
17287
17373
  const focusActive = () => {
17288
17374
  const item = items[activeIndex];
@@ -17457,23 +17543,86 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17457
17543
  if (target instanceof Element) return target;
17458
17544
  return target instanceof Node ? target.parentElement : null;
17459
17545
  }
17546
+ function controlDescription(element) {
17547
+ const typed = element;
17548
+ const read = (name) => typeof element.getAttribute === "function" ? element.getAttribute(name) : null;
17549
+ return [typed.type, read("aria-label"), read("title"), read("data-testid")].filter((value) => typeof value === "string").join(" ").toLowerCase();
17550
+ }
17460
17551
  function buttonText(button) {
17461
- return [
17462
- button.type,
17463
- button.getAttribute("aria-label"),
17464
- button.getAttribute("title"),
17465
- button.getAttribute("data-testid")
17466
- ].filter((value) => typeof value === "string").join(" ").toLowerCase();
17552
+ return controlDescription(button);
17553
+ }
17554
+ function isOwnedRendererControl(element) {
17555
+ return element.hasAttribute(CONTROL_ATTRIBUTE) || element.hasAttribute("data-codexhost-model-control") || element.hasAttribute("data-codexhost-permission-mode-control") || element.hasAttribute("data-codexhost-usage-control") || element.hasAttribute("data-codexhost-credits-control") || element.hasAttribute("data-codexhost-harness-command-control");
17467
17556
  }
17468
17557
  function isComposerSubmitButton(button) {
17469
17558
  if (button.type === "submit") return true;
17470
17559
  return /(^|\s)(send|submit|发送|提交)(\s|$)/u.test(buttonText(button));
17471
17560
  }
17561
+ var VOICE_CONTROL_PATTERN = /(dictat|microphone|speech(?:[-_\s]?to[-_\s]?text)?|voice[-_\s]?input|(^|\s)voice(\s|$)|composer[-_](?:speech|dictat|mic)|语音|听写|麦克风|pause|暂停|stop recording|stop dictation|停止录音|停止听写|(^|\s)stop(\s|$))/iu;
17562
+ var CANCEL_CONTROL_PATTERN = /(cancel|discard|close|dismiss|取消|关闭|丢弃)/iu;
17563
+ var ATTACH_CONTROL_PATTERN = /(add files|attach files|attach file|attachment|composer[-_]attach|添加文件|附件)/iu;
17564
+ var TRAILING_ACTION_WALK_DEPTH = 3;
17565
+ function isComposerCancelButton(element) {
17566
+ if (isOwnedRendererControl(element)) return false;
17567
+ return CANCEL_CONTROL_PATTERN.test(controlDescription(element));
17568
+ }
17569
+ function isComposerVoiceButton(element) {
17570
+ if (isOwnedRendererControl(element) || isComposerCancelButton(element)) return false;
17571
+ const description = controlDescription(element);
17572
+ if (/(^|\s)(send|submit|发送|提交)(\s|$)/u.test(description)) return false;
17573
+ return VOICE_CONTROL_PATTERN.test(description);
17574
+ }
17575
+ function isComposerAttachButton(element) {
17576
+ if (isOwnedRendererControl(element)) return false;
17577
+ return ATTACH_CONTROL_PATTERN.test(controlDescription(element));
17578
+ }
17579
+ function isComposerTrailingActionButton(element) {
17580
+ return isComposerVoiceButton(element) || isComposerSubmitButton(element);
17581
+ }
17582
+ function isTrailingActionNode(element) {
17583
+ if (isComposerCancelButton(element)) return false;
17584
+ if (isComposerTrailingActionButton(element)) return true;
17585
+ if (typeof element.querySelectorAll !== "function") return false;
17586
+ const buttons = [...element.querySelectorAll("button")];
17587
+ return buttons.length > 0 && buttons.every((button) => isComposerTrailingActionButton(button));
17588
+ }
17589
+ function attachControlWithin(root) {
17590
+ if (isComposerAttachButton(root)) return root;
17591
+ if (typeof root.querySelectorAll !== "function") return null;
17592
+ const matches = [
17593
+ ...root.querySelectorAll("button, [aria-label], [title], [data-testid]")
17594
+ ].filter(isComposerAttachButton);
17595
+ return matches.length === 1 ? matches[0] ?? null : null;
17596
+ }
17472
17597
  function sendButtonWithin(root) {
17473
17598
  return [...root.querySelectorAll("button")].find(
17474
17599
  (button) => isComposerSubmitButton(button)
17475
17600
  ) ?? null;
17476
17601
  }
17602
+ function leftmostTrailingSibling(container, before) {
17603
+ for (const child of container.children) {
17604
+ if (child === before) break;
17605
+ if (typeof child.hasAttribute !== "function") continue;
17606
+ const element = child;
17607
+ if (isOwnedRendererControl(element) || isComposerCancelButton(element)) continue;
17608
+ if (isTrailingActionNode(element)) return element;
17609
+ }
17610
+ return null;
17611
+ }
17612
+ function trailingActionAnchor(sendButton) {
17613
+ let container = sendButton.parentElement;
17614
+ let before = sendButton;
17615
+ for (let depth = 0; container && depth < TRAILING_ACTION_WALK_DEPTH; depth += 1) {
17616
+ if (typeof container.matches === "function" && container.matches(CODEX_COMPOSER_SELECTOR)) {
17617
+ break;
17618
+ }
17619
+ const candidate = leftmostTrailingSibling(container, before);
17620
+ if (candidate) return candidate;
17621
+ before = container;
17622
+ container = container.parentElement;
17623
+ }
17624
+ return sendButton;
17625
+ }
17477
17626
  function editorForElement(element) {
17478
17627
  return element.matches(EDITOR_SELECTOR) ? element : element.closest(EDITOR_SELECTOR);
17479
17628
  }
@@ -17619,7 +17768,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17619
17768
  for (const child of parent.children) {
17620
17769
  if (typeof child.hasAttribute !== "function") continue;
17621
17770
  const element = child;
17622
- if (element.hasAttribute("data-codexhost-credits-control") || element.hasAttribute("data-codexhost-harness-command-control")) {
17771
+ if (element.hasAttribute("data-codexhost-credits-control") || element.hasAttribute("data-codexhost-harness-command-control") || isTrailingActionNode(element)) {
17623
17772
  continue;
17624
17773
  }
17625
17774
  return element;
@@ -17632,12 +17781,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17632
17781
  const parent = current.parentElement;
17633
17782
  if (!parent) break;
17634
17783
  const first = firstMaterialChild(parent);
17635
- if (first && first !== current && !first.contains(usageRoot)) return first;
17784
+ if (first && first !== current && !first.contains(usageRoot)) {
17785
+ return attachControlWithin(first) ?? first;
17786
+ }
17636
17787
  if (parent === composer) break;
17637
17788
  current = parent;
17638
17789
  }
17639
17790
  return null;
17640
17791
  }
17792
+ function refreshTrailingClusterPlacement(control) {
17793
+ const sendButton = control.sendButton;
17794
+ const modelRoot = control.modelPicker?.root;
17795
+ const agentRoot = control.root ?? control.picker?.root;
17796
+ if (!sendButton || !modelRoot || !agentRoot) return;
17797
+ const anchor = trailingActionAnchor(sendButton);
17798
+ const parent = anchor.parentElement;
17799
+ if (!parent || typeof parent.insertBefore !== "function") return;
17800
+ if (modelRoot.parentElement === parent && agentRoot.parentElement === parent && modelRoot.nextElementSibling === agentRoot && agentRoot.nextElementSibling === anchor) {
17801
+ return;
17802
+ }
17803
+ parent.insertBefore(modelRoot, anchor);
17804
+ parent.insertBefore(agentRoot, anchor);
17805
+ }
17641
17806
  function refreshUsagePlacement(control) {
17642
17807
  const anchor = usagePlacementAnchor(control);
17643
17808
  if (!anchor) {
@@ -17690,6 +17855,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17690
17855
  }
17691
17856
  function reconcileComposerNativeControls(control, hideModel, hidePermissionMode) {
17692
17857
  refreshNativeModelControl(control);
17858
+ refreshTrailingClusterPlacement(control);
17693
17859
  refreshUsagePlacement(control);
17694
17860
  refreshNativePermissionModeControl(control);
17695
17861
  setNativeControlHidden(control.nativeModelControl, hideModel);
@@ -17717,7 +17883,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17717
17883
  const toolbar = sendButton.parentElement;
17718
17884
  const harnessCommands = mountRendererHarnessCommandControl(
17719
17885
  toolbar ?? composer,
17720
- sendButton,
17886
+ trailingActionAnchor(sendButton),
17721
17887
  onSelectCommand
17722
17888
  );
17723
17889
  const permissionParent = nativePermissionModeControl?.element.parentElement;
@@ -17726,12 +17892,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17726
17892
  } else {
17727
17893
  composer.append(permissionModePicker.root);
17728
17894
  }
17729
- if (toolbar) {
17730
- toolbar.insertBefore(modelPicker.root, sendButton);
17731
- toolbar.insertBefore(picker.root, sendButton);
17732
- } else {
17733
- composer.append(modelPicker.root, picker.root);
17734
- }
17895
+ if (!toolbar) composer.append(modelPicker.root, picker.root);
17735
17896
  const control = {
17736
17897
  composer,
17737
17898
  root: picker.root,
@@ -17747,6 +17908,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17747
17908
  sendButton,
17748
17909
  sendDisabledBeforeSwitch: null
17749
17910
  };
17911
+ refreshTrailingClusterPlacement(control);
17750
17912
  refreshUsagePlacement(control);
17751
17913
  return control;
17752
17914
  }
@@ -17870,6 +18032,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17870
18032
  isConnected() {
17871
18033
  return this.element.isConnected;
17872
18034
  }
18035
+ hostId() {
18036
+ return sidebarThreadAttributes(this.element)?.hostId ?? null;
18037
+ }
17873
18038
  threadId() {
17874
18039
  return threadIdFromSidebarRowElement(this.element);
17875
18040
  }
@@ -17939,7 +18104,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17939
18104
  const observer = new MutationObserver(onChange);
17940
18105
  observer.observe(this.root, {
17941
18106
  attributes: true,
17942
- attributeFilter: [SIDEBAR_THREAD_ID_ATTRIBUTE],
18107
+ attributeFilter: [SIDEBAR_THREAD_ID_ATTRIBUTE, SIDEBAR_THREAD_HOST_ID_ATTRIBUTE],
17943
18108
  childList: true,
17944
18109
  subtree: true
17945
18110
  });
@@ -17960,78 +18125,88 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17960
18125
  const ownershipRetryTimers = /* @__PURE__ */ new Map();
17961
18126
  let disposed = false;
17962
18127
  let scanScheduled = false;
18128
+ const ownershipKey = (hostId, threadId) => JSON.stringify([hostId, threadId]);
17963
18129
  const scheduleScan = () => {
17964
18130
  if (disposed || scanScheduled) return;
17965
18131
  scanScheduled = true;
17966
18132
  queueMicrotask(scan);
17967
18133
  };
17968
- const clearOwnershipRetry = (threadId) => {
17969
- const timer = ownershipRetryTimers.get(threadId);
18134
+ const clearOwnershipRetry = (key) => {
18135
+ const timer = ownershipRetryTimers.get(key);
17970
18136
  if (timer !== void 0) clearTimeout(timer);
17971
- ownershipRetryTimers.delete(threadId);
17972
- ownershipRetryAttempts.delete(threadId);
17973
- provisionalCodex.delete(threadId);
18137
+ ownershipRetryTimers.delete(key);
18138
+ ownershipRetryAttempts.delete(key);
18139
+ provisionalCodex.delete(key);
17974
18140
  };
17975
- const scheduleOwnershipRetry = (threadId) => {
17976
- if (disposed || !provisionalCodex.has(threadId) || pending.has(threadId) || ownershipRetryTimers.has(threadId)) {
18141
+ const scheduleOwnershipRetry = (hostId, threadId) => {
18142
+ const key = ownershipKey(hostId, threadId);
18143
+ if (disposed || !provisionalCodex.has(key) || pending.has(key) || ownershipRetryTimers.has(key)) {
17977
18144
  return;
17978
18145
  }
17979
- const attempt = ownershipRetryAttempts.get(threadId) ?? 0;
18146
+ const attempt = ownershipRetryAttempts.get(key) ?? 0;
17980
18147
  const delay = OWNERSHIP_RETRY_DELAYS_MS[attempt];
17981
18148
  if (delay === void 0) return;
17982
- ownershipRetryAttempts.set(threadId, attempt + 1);
18149
+ ownershipRetryAttempts.set(key, attempt + 1);
17983
18150
  const timer = setTimeout(() => {
17984
- ownershipRetryTimers.delete(threadId);
18151
+ ownershipRetryTimers.delete(key);
17985
18152
  if (disposed) return;
17986
- failed.delete(threadId);
17987
- ownershipByThread.delete(threadId);
18153
+ failed.delete(key);
18154
+ ownershipByThread.delete(key);
17988
18155
  scheduleScan();
17989
18156
  }, delay);
17990
- ownershipRetryTimers.set(threadId, timer);
18157
+ ownershipRetryTimers.set(key, timer);
17991
18158
  };
17992
- const requestOwnership = (threadIds, client) => {
17993
- for (const threadId of threadIds) pending.add(threadId);
18159
+ const requestOwnership = (hostId, threadIds, client) => {
18160
+ for (const threadId of threadIds) pending.add(ownershipKey(hostId, threadId));
17994
18161
  let succeeded = false;
17995
18162
  void Promise.resolve().then(() => client.listThreadOwnership({ threadIds })).then(({ threads }) => {
17996
18163
  if (disposed) return;
17997
18164
  for (const ownership of threads) {
17998
- ownershipByThread.set(ownership.threadId, rendererAgentForThreadOwnership(ownership));
17999
- failed.delete(ownership.threadId);
18165
+ const key = ownershipKey(hostId, ownership.threadId);
18166
+ ownershipByThread.set(key, rendererAgentForThreadOwnership(ownership));
18167
+ failed.delete(key);
18000
18168
  if (ownership.owner === "codex") {
18001
- provisionalCodex.add(ownership.threadId);
18002
- scheduleOwnershipRetry(ownership.threadId);
18169
+ provisionalCodex.add(key);
18170
+ scheduleOwnershipRetry(hostId, ownership.threadId);
18003
18171
  } else {
18004
- clearOwnershipRetry(ownership.threadId);
18172
+ clearOwnershipRetry(key);
18005
18173
  }
18006
18174
  }
18007
18175
  succeeded = true;
18008
18176
  }).catch(() => {
18009
18177
  if (disposed) return;
18010
- for (const threadId of threadIds) failed.add(threadId);
18178
+ for (const threadId of threadIds) failed.add(ownershipKey(hostId, threadId));
18011
18179
  }).finally(() => {
18012
- for (const threadId of threadIds) pending.delete(threadId);
18013
- for (const threadId of threadIds) scheduleOwnershipRetry(threadId);
18180
+ for (const threadId of threadIds) pending.delete(ownershipKey(hostId, threadId));
18181
+ for (const threadId of threadIds) scheduleOwnershipRetry(hostId, threadId);
18014
18182
  if (succeeded) scheduleScan();
18015
18183
  });
18016
18184
  };
18017
18185
  const scan = () => {
18018
18186
  scanScheduled = false;
18019
18187
  if (disposed) return;
18020
- const unresolved = /* @__PURE__ */ new Set();
18188
+ const unresolvedByHost = /* @__PURE__ */ new Map();
18021
18189
  for (const row of dom.rows()) {
18022
18190
  if (!row.isConnected()) {
18023
18191
  row.clear();
18024
18192
  continue;
18025
18193
  }
18194
+ const hostId = row.hostId();
18195
+ if (!hostId) {
18196
+ row.clear();
18197
+ continue;
18198
+ }
18026
18199
  const threadId = hostThreadIdSchema.safeParse(row.threadId());
18027
18200
  const localAgent = options.getLocalAgent?.({
18201
+ hostId,
18028
18202
  threadId: threadId.success ? threadId.data : null,
18029
18203
  draftId: row.draftId()
18030
18204
  });
18031
18205
  if (localAgent !== null && localAgent !== void 0) {
18032
18206
  if (threadId.success) {
18033
- ownershipByThread.set(threadId.data, localAgent === "codex" ? null : localAgent);
18034
- clearOwnershipRetry(threadId.data);
18207
+ const key2 = ownershipKey(hostId, threadId.data);
18208
+ ownershipByThread.set(key2, localAgent === "codex" ? null : localAgent);
18209
+ clearOwnershipRetry(key2);
18035
18210
  }
18036
18211
  if (localAgent === "codex") row.clear();
18037
18212
  else row.render(localAgent);
@@ -18041,22 +18216,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18041
18216
  row.clear();
18042
18217
  continue;
18043
18218
  }
18044
- if (ownershipByThread.has(threadId.data)) {
18045
- const agent = ownershipByThread.get(threadId.data);
18219
+ const key = ownershipKey(hostId, threadId.data);
18220
+ if (ownershipByThread.has(key)) {
18221
+ const agent = ownershipByThread.get(key);
18046
18222
  if (agent) row.render(agent);
18047
18223
  else row.clear();
18048
18224
  continue;
18049
18225
  }
18050
18226
  row.clear();
18051
- if (!pending.has(threadId.data) && !failed.has(threadId.data)) {
18227
+ if (!pending.has(key) && !failed.has(key)) {
18228
+ let unresolved = unresolvedByHost.get(hostId);
18229
+ if (!unresolved) {
18230
+ unresolved = /* @__PURE__ */ new Set();
18231
+ unresolvedByHost.set(hostId, unresolved);
18232
+ }
18052
18233
  unresolved.add(threadId.data);
18053
18234
  }
18054
18235
  }
18055
- const client = options.getClient();
18056
- if (!client) return;
18057
- const threadIds = [...unresolved];
18058
- for (let index = 0; index < threadIds.length; index += THREAD_OWNERSHIP_LIST_MAX_LENGTH) {
18059
- requestOwnership(threadIds.slice(index, index + THREAD_OWNERSHIP_LIST_MAX_LENGTH), client);
18236
+ for (const [hostId, unresolved] of unresolvedByHost) {
18237
+ const client = options.getClient(hostId);
18238
+ if (!client) continue;
18239
+ const threadIds = [...unresolved];
18240
+ for (let index = 0; index < threadIds.length; index += THREAD_OWNERSHIP_LIST_MAX_LENGTH) {
18241
+ requestOwnership(
18242
+ hostId,
18243
+ threadIds.slice(index, index + THREAD_OWNERSHIP_LIST_MAX_LENGTH),
18244
+ client
18245
+ );
18246
+ }
18060
18247
  }
18061
18248
  };
18062
18249
  const stopObserving = dom.observe(scheduleScan);
@@ -18064,12 +18251,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18064
18251
  return {
18065
18252
  refresh() {
18066
18253
  failed.clear();
18067
- for (const threadId of provisionalCodex) {
18068
- const timer = ownershipRetryTimers.get(threadId);
18254
+ for (const key of provisionalCodex) {
18255
+ const timer = ownershipRetryTimers.get(key);
18069
18256
  if (timer !== void 0) clearTimeout(timer);
18070
- ownershipRetryTimers.delete(threadId);
18071
- ownershipRetryAttempts.delete(threadId);
18072
- ownershipByThread.delete(threadId);
18257
+ ownershipRetryTimers.delete(key);
18258
+ ownershipRetryAttempts.delete(key);
18259
+ ownershipByThread.delete(key);
18073
18260
  }
18074
18261
  scheduleScan();
18075
18262
  },
@@ -18467,28 +18654,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18467
18654
  }
18468
18655
  return `${CLAUDE_CODE_TRANSPORT_MODEL_PREFIX}${parsedModel.id}${parsedPermissionMode ? `@${parsedPermissionMode}` : ""}`;
18469
18656
  }
18470
- function grokTransportModelId(model, thinkingOptionId) {
18657
+ function grokTransportModelId(model, permissionModeId, thinkingOptionId) {
18471
18658
  if (!model) {
18472
- if (thinkingOptionId) throw new Error("Grok transport Thinking requires a Model Ref");
18659
+ if (permissionModeId || thinkingOptionId) {
18660
+ throw new Error("Grok transport configuration requires a Model Ref");
18661
+ }
18473
18662
  return GROK_TRANSPORT_MODEL_ID;
18474
18663
  }
18475
18664
  const parsedModel = harnessModelRefSchema.parse(model);
18665
+ const parsedPermissionMode = permissionModeId ? harnessPermissionModeIdSchema.parse(permissionModeId) : void 0;
18476
18666
  const parsedThinking = thinkingOptionId ? harnessThinkingOptionIdSchema.parse(thinkingOptionId) : void 0;
18477
- return `${GROK_TRANSPORT_MODEL_PREFIX}${parsedModel.id}${parsedThinking ? `@@${parsedThinking}` : ""}`;
18667
+ if (parsedThinking) {
18668
+ return `${GROK_TRANSPORT_MODEL_PREFIX}${parsedModel.id}@${parsedPermissionMode ?? ""}@${parsedThinking}`;
18669
+ }
18670
+ return `${GROK_TRANSPORT_MODEL_PREFIX}${parsedModel.id}${parsedPermissionMode ? `@${parsedPermissionMode}` : ""}`;
18478
18671
  }
18479
18672
  function decodeGrokTransportModelId(value) {
18480
18673
  if (value === GROK_TRANSPORT_MODEL_ID) return {};
18481
18674
  if (typeof value !== "string" || !value.startsWith(GROK_TRANSPORT_MODEL_PREFIX)) return null;
18482
18675
  const components = value.slice(GROK_TRANSPORT_MODEL_PREFIX.length).split("@");
18483
- if (components.length !== 1 && components.length !== 3) return null;
18484
- const [modelId, emptyPermissionMode, thinkingOptionId] = components;
18485
- if (components.length === 3 && (emptyPermissionMode !== "" || !thinkingOptionId)) return null;
18676
+ if (components.length < 1 || components.length > 3) return null;
18677
+ const [modelId, permissionModeId, thinkingOptionId] = components;
18678
+ if (components.length === 2 && !permissionModeId) return null;
18679
+ if (components.length === 3 && !thinkingOptionId) return null;
18486
18680
  const model = harnessModelRefSchema.safeParse({ id: modelId });
18487
18681
  if (!model.success) return null;
18682
+ const permissionMode = permissionModeId ? harnessPermissionModeIdSchema.safeParse(permissionModeId) : null;
18683
+ if (permissionMode && !permissionMode.success) return null;
18488
18684
  const thinking = thinkingOptionId ? harnessThinkingOptionIdSchema.safeParse(thinkingOptionId) : null;
18489
18685
  if (thinking && !thinking.success) return null;
18490
18686
  return {
18491
18687
  model: model.data,
18688
+ ...permissionMode?.success ? { permissionModeId: permissionMode.data } : {},
18492
18689
  ...thinking?.success ? { thinkingOptionId: thinking.data } : {}
18493
18690
  };
18494
18691
  }
@@ -18550,22 +18747,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18550
18747
  }
18551
18748
  return hostThreadIdSchema.parse(target[1]);
18552
18749
  }
18553
- function hasPrewarmMethod(value) {
18554
- return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.prewarmThreadStart === "function";
18555
- }
18556
- function isActiveRequestManager(value) {
18557
- if (!isRecord5(value) || typeof value.sendRequest !== "function") return false;
18558
- const source = Function.prototype.toString.call(value.sendRequest);
18559
- return source.includes("send-cli-request-for-host") && hasPrewarmMethod(value.requestClient);
18560
- }
18561
- function matchesCurrentPrewarmSignature(target) {
18562
- const bridge = target.requestClient ?? target;
18563
- const stableApiShape = bridge.hostId === "local" && typeof bridge.sendRequest === "function" && typeof bridge.prewarmThreadStart === "function" && typeof bridge.enqueueRequest === "function";
18564
- if (stableApiShape) return true;
18565
- const prewarm = target.prewarmThreadStart ?? target.requestClient?.prewarmThreadStart;
18566
- if (!prewarm) return false;
18567
- const source = Function.prototype.toString.call(prewarm);
18568
- return source.includes("enqueueRequest") && source.includes("thread-prewarm-start") || source.includes("prewarm-thread-start-for-host");
18750
+ function isCurrentRequestBridge(value) {
18751
+ return isRecord5(value) && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.sendRequest === "function" && typeof value.prewarmThreadStart === "function" && typeof value.enqueueRequest === "function";
18569
18752
  }
18570
18753
  function findActivePrewarmTargets(root) {
18571
18754
  const editor = root.querySelector(
@@ -18595,10 +18778,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18595
18778
  const hookState = hook.memoizedState;
18596
18779
  if (isRecord5(hookState)) {
18597
18780
  const requestClient = hookState.requestClient;
18598
- if (isActiveRequestManager(hookState) && matchesCurrentPrewarmSignature(hookState)) {
18599
- targets.add(hookState);
18600
- } else if (hasPrewarmMethod(requestClient) && matchesCurrentPrewarmSignature(requestClient)) {
18601
- targets.add(typeof hookState.sendRequest === "function" ? hookState : requestClient);
18781
+ const bridge = isCurrentRequestBridge(requestClient) ? requestClient : isCurrentRequestBridge(hookState) ? hookState : null;
18782
+ if (bridge) {
18783
+ targets.add(typeof hookState.sendRequest === "function" ? hookState : bridge);
18602
18784
  }
18603
18785
  }
18604
18786
  hook = typeof hook.next === "object" && hook.next !== null ? hook.next : null;
@@ -18651,10 +18833,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18651
18833
  return false;
18652
18834
  }
18653
18835
  }
18654
- function findComposerModelTarget(composer) {
18655
- const conversationThreadId = findComposerConversationThreadId(composer);
18656
- if (conversationThreadId === null) return null;
18657
- if (conversationThreadId !== void 0) return ["conversation", conversationThreadId];
18836
+ function findComposerDomIdentity(composer) {
18837
+ const children = Array.from(composer.children ?? []);
18838
+ const portals = children.filter((child) => child.hasAttribute("data-above-composer-portal"));
18839
+ if (portals.length === 0) return { kind: "unsupported" };
18840
+ if (portals.length !== 1) return { kind: "ambiguous" };
18841
+ const value = portals[0]?.getAttribute("data-above-composer-conversation-id");
18842
+ if (value === null) return { kind: "draft" };
18843
+ const candidate = hostThreadIdSchema.safeParse(value);
18844
+ return candidate.success ? { kind: "conversation", threadId: candidate.data } : { kind: "ambiguous" };
18845
+ }
18846
+ function findComposerDraftIds(composer) {
18658
18847
  const draftIds = /* @__PURE__ */ new Set();
18659
18848
  let fiber = findComposerFiber(composer);
18660
18849
  for (let depth = 0; fiber && depth < 120; depth += 1) {
@@ -18669,6 +18858,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18669
18858
  const parent = fiber.return;
18670
18859
  fiber = (typeof parent === "object" || typeof parent === "function") && parent !== null ? parent : null;
18671
18860
  }
18861
+ return draftIds;
18862
+ }
18863
+ function findComposerModelTarget(composer) {
18864
+ const draftIds = findComposerDraftIds(composer);
18865
+ const domIdentity = findComposerDomIdentity(composer);
18866
+ if (domIdentity.kind === "ambiguous") return null;
18867
+ if (domIdentity.kind === "conversation") {
18868
+ return ["conversation", domIdentity.threadId];
18869
+ }
18870
+ if (domIdentity.kind === "draft") {
18871
+ return draftIds.size === 1 ? ["default", draftIds.values().next().value] : null;
18872
+ }
18873
+ const conversationThreadId = findComposerConversationThreadId(composer);
18874
+ if (conversationThreadId === null) return null;
18875
+ if (conversationThreadId !== void 0) return ["conversation", conversationThreadId];
18672
18876
  if (draftIds.size !== 1) return null;
18673
18877
  return ["default", draftIds.values().next().value];
18674
18878
  }
@@ -18676,7 +18880,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18676
18880
  return isRecord5(value) && value.state === "ready";
18677
18881
  }
18678
18882
  function isDraftPrewarmPolicyReady(value) {
18679
- return isRecord5(value) && value.state === "ready" && typeof value.select === "function" && typeof value.clear === "function";
18883
+ return isRecord5(value) && value.state === "ready" && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.select === "function" && typeof value.clear === "function";
18884
+ }
18885
+ function prewarmTargetHostId(target) {
18886
+ const bridge = target.requestClient ?? target;
18887
+ const hostId = target.getHostId?.() ?? bridge.hostId;
18888
+ return typeof hostId === "string" && hostId.length > 0 ? hostId : null;
18889
+ }
18890
+ function rendererRequestTargetsForHost(targets, hostId) {
18891
+ const matching = targets.filter((target) => prewarmTargetHostId(target) === hostId);
18892
+ return matching.length === 1 ? matching : null;
18893
+ }
18894
+ function activeRendererDraftPrewarmTargets(policy, targets) {
18895
+ if (!isDraftPrewarmPolicyReady(policy)) return null;
18896
+ return rendererRequestTargetsForHost(targets, policy.hostId);
18897
+ }
18898
+ function resolveRendererRequestRoute(policy, discoveredTargets, previous) {
18899
+ const activeTargets = activeRendererDraftPrewarmTargets(policy, discoveredTargets);
18900
+ if (isDraftPrewarmPolicyReady(policy) && activeTargets) {
18901
+ return { policy, targets: activeTargets };
18902
+ }
18903
+ return discoveredTargets.length === 0 && isDraftPrewarmPolicyReady(policy) && previous?.policy === policy ? previous : null;
18904
+ }
18905
+ function createRendererRequestRouteResolver(readPolicy, discoverTargets) {
18906
+ let route = null;
18907
+ return {
18908
+ resolve() {
18909
+ route = resolveRendererRequestRoute(readPolicy(), discoverTargets(), route);
18910
+ return route;
18911
+ },
18912
+ clear() {
18913
+ route = null;
18914
+ }
18915
+ };
18680
18916
  }
18681
18917
  async function waitForRendererDraftPrewarmPolicy(target) {
18682
18918
  const deadline = Date.now() + DRAFT_PREWARM_POLICY_WAIT_TIMEOUT_MS;
@@ -18691,7 +18927,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18691
18927
  }
18692
18928
  }
18693
18929
  function modelSelectionForAgent(officialSelection, reasoningEffort, agent, model, thinkingOptionId, permissionModeId) {
18694
- const transportModelId = agent === "pi" ? piTransportModelId(model, thinkingOptionId) : agent === "claude-code" ? claudeTransportModelId(model, permissionModeId, thinkingOptionId) : agent === "deepseek-harness" ? deepSeekHarnessTransportModelId(model) : agent === "grok" ? grokTransportModelId(model, thinkingOptionId) : transportModelIdForAgent(agent);
18930
+ const transportModelId = agent === "pi" ? piTransportModelId(model, thinkingOptionId) : agent === "claude-code" ? claudeTransportModelId(model, permissionModeId, thinkingOptionId) : agent === "deepseek-harness" ? deepSeekHarnessTransportModelId(model) : agent === "grok" ? grokTransportModelId(model, permissionModeId, thinkingOptionId) : transportModelIdForAgent(agent);
18695
18931
  return transportModelId ? { model: transportModelId, reasoningEffort } : officialSelection;
18696
18932
  }
18697
18933
  function installCurrentRendererAdapter() {
@@ -18717,13 +18953,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18717
18953
  }
18718
18954
  });
18719
18955
  const usageSubscription = createThreadUsageSubscriptionRelay();
18956
+ const requestRouteResolver = createRendererRequestRouteResolver(
18957
+ () => window.__codexhostDraftPrewarmPolicyV1,
18958
+ () => findActivePrewarmTargets(document)
18959
+ );
18960
+ const currentRequestRoute = () => requestRouteResolver.resolve();
18961
+ const clientsByTarget = /* @__PURE__ */ new WeakMap();
18962
+ const modelClientForTargets = (targets) => {
18963
+ const target = targets[0];
18964
+ if (targets.length !== 1 || !target) return null;
18965
+ const cached2 = clientsByTarget.get(target);
18966
+ if (cached2) return cached2;
18967
+ const client = createRendererModelClient([target]);
18968
+ if (client) clientsByTarget.set(target, client);
18969
+ return client;
18970
+ };
18720
18971
  const currentModelClient = () => {
18721
- const client = createRendererModelClient(findActivePrewarmTargets(document));
18972
+ const client = modelClientForTargets(currentRequestRoute()?.targets ?? []);
18722
18973
  if (!client) throw new Error("Renderer Model request manager is unavailable");
18723
18974
  usageSubscription.connect(client);
18724
18975
  return client;
18725
18976
  };
18726
18977
  const modelControl = Object.freeze({
18978
+ currentHostId: () => currentRequestRoute()?.policy.hostId ?? null,
18979
+ clientForHost(hostId) {
18980
+ const targets = rendererRequestTargetsForHost(findActivePrewarmTargets(document), hostId);
18981
+ return modelClientForTargets(targets ?? []);
18982
+ },
18727
18983
  forkThread: (input) => currentModelClient().forkThread(input),
18728
18984
  inspectHarness: (input) => currentModelClient().inspectHarness(input),
18729
18985
  inspectThread: (input) => currentModelClient().inspectThread(input),
@@ -18754,10 +19010,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18754
19010
  });
18755
19011
  let routingPolicy = null;
18756
19012
  let policyTimer = null;
19013
+ let desiredCarrier = null;
18757
19014
  const captureRoutingPolicy = () => {
18758
- const discovered = window.__codexhostDraftPrewarmPolicyV1;
18759
- if (!isDraftPrewarmPolicyReady(discovered)) return false;
18760
- routingPolicy = discovered;
19015
+ const route = currentRequestRoute();
19016
+ if (!route) return false;
19017
+ routingPolicy = route.policy;
19018
+ routingPolicy.select(desiredCarrier);
18761
19019
  if (policyTimer !== null) {
18762
19020
  window.clearInterval(policyTimer);
18763
19021
  policyTimer = null;
@@ -18769,8 +19027,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18769
19027
  updateStatus("installing", "draft-routing-policy-unavailable", null);
18770
19028
  policyTimer = window.setInterval(captureRoutingPolicy, DRAFT_PREWARM_POLICY_POLL_INTERVAL_MS);
18771
19029
  }
19030
+ const handleRoutingPolicyChange = () => {
19031
+ captureRoutingPolicy();
19032
+ };
19033
+ window.addEventListener("codexhost:draft-prewarm-policy-changed", handleRoutingPolicyChange);
18772
19034
  const applyAgent = (agent, model, thinkingOptionId, permissionModeId) => {
18773
- if (disposed || routingPolicy === null) return false;
19035
+ if (disposed) return false;
18774
19036
  const selection = modelSelectionForAgent(
18775
19037
  null,
18776
19038
  null,
@@ -18781,7 +19043,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18781
19043
  );
18782
19044
  const carrier = selection?.model;
18783
19045
  if (carrier !== null && carrier !== void 0 && typeof carrier !== "string") return false;
18784
- if (routingPolicy.select(carrier ?? null)) {
19046
+ desiredCarrier = carrier ?? null;
19047
+ const route = currentRequestRoute();
19048
+ if (!route) return false;
19049
+ routingPolicy = route.policy;
19050
+ if (route.policy.select(desiredCarrier)) {
18785
19051
  modelUpdates += 1;
18786
19052
  liveStatus.modelUpdates = modelUpdates;
18787
19053
  }
@@ -18795,8 +19061,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18795
19061
  if (disposed) return;
18796
19062
  disposed = true;
18797
19063
  if (policyTimer !== null) window.clearInterval(policyTimer);
19064
+ window.removeEventListener(
19065
+ "codexhost:draft-prewarm-policy-changed",
19066
+ handleRoutingPolicyChange
19067
+ );
18798
19068
  routingPolicy?.select(null);
18799
19069
  routingPolicy = null;
19070
+ requestRouteResolver.clear();
18800
19071
  forkControl.dispose();
18801
19072
  usageSubscription.dispose();
18802
19073
  }
@@ -18848,9 +19119,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18848
19119
  const model = harnessModelRefSchema.safeParse(value.model);
18849
19120
  if (!model.success) return void 0;
18850
19121
  const thinkingOptionId = harnessThinkingOptionIdSchema.safeParse(value.thinkingOptionId);
19122
+ const permissionModeId = harnessPermissionModeIdSchema.safeParse(value.permissionModeId);
18851
19123
  return {
18852
19124
  model: model.data,
18853
- ...thinkingOptionId.success ? { thinkingOptionId: thinkingOptionId.data } : {}
19125
+ ...thinkingOptionId.success ? { thinkingOptionId: thinkingOptionId.data } : {},
19126
+ ...permissionModeId.success ? { permissionModeId: permissionModeId.data } : {}
18854
19127
  };
18855
19128
  }
18856
19129
  function readPreference(storage) {
@@ -18890,15 +19163,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18890
19163
  const agent = readPreference(storage)?.lastAgent;
18891
19164
  return agent && enabledAgents.has(agent) ? agent : void 0;
18892
19165
  }
18893
- function readNewThreadExternalConfigurationPreference(agent, catalog, storage = rendererStorage2()) {
19166
+ function readNewThreadExternalConfigurationPreference(agent, catalog, permissionModes, storage = rendererStorage2()) {
18894
19167
  const preference = readPreference(storage)?.externalByAgent[agent];
18895
19168
  if (!preference) return void 0;
18896
19169
  const catalogModel = catalog.models.find(({ ref }) => ref.id === preference.model.id);
18897
19170
  if (!catalogModel) return void 0;
18898
19171
  const thinkingOptionId = preference.thinkingOptionId && catalogModel.supportedThinkingOptionIds?.includes(preference.thinkingOptionId) ? preference.thinkingOptionId : void 0;
19172
+ const permissionModeId = preference.permissionModeId && permissionModes?.modes.some(({ id }) => id === preference.permissionModeId) ? preference.permissionModeId : void 0;
18899
19173
  return {
18900
19174
  model: catalogModel.ref,
18901
- ...thinkingOptionId ? { thinkingOptionId } : {}
19175
+ ...thinkingOptionId ? { thinkingOptionId } : {},
19176
+ ...permissionModeId ? { permissionModeId } : {}
18902
19177
  };
18903
19178
  }
18904
19179
  function writeNewThreadAgentPreference(agent, storage = rendererStorage2()) {
@@ -18912,7 +19187,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18912
19187
  storage
18913
19188
  );
18914
19189
  }
18915
- function writeNewThreadExternalConfigurationPreference(agent, model, thinkingOptionId, storage = rendererStorage2()) {
19190
+ function writeNewThreadExternalConfigurationPreference(agent, model, thinkingOptionId, permissionModeId, storage = rendererStorage2()) {
18916
19191
  const current = readPreference(storage);
18917
19192
  writePreference(
18918
19193
  {
@@ -18922,7 +19197,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18922
19197
  ...current?.externalByAgent,
18923
19198
  [agent]: {
18924
19199
  model: harnessModelRefSchema.parse(model),
18925
- ...thinkingOptionId ? { thinkingOptionId: harnessThinkingOptionIdSchema.parse(thinkingOptionId) } : {}
19200
+ ...thinkingOptionId ? { thinkingOptionId: harnessThinkingOptionIdSchema.parse(thinkingOptionId) } : {},
19201
+ ...permissionModeId ? { permissionModeId: harnessPermissionModeIdSchema.parse(permissionModeId) } : {}
18926
19202
  }
18927
19203
  }
18928
19204
  },
@@ -19122,6 +19398,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19122
19398
  runtimeCapabilityNotInstalled: "This runtime capability is not installed yet.",
19123
19399
  connectionsDescription: "Inspect the adapter and each Agent runtime used by the picker. Failed checks keep their error details here.",
19124
19400
  connectionAdapter: "Renderer adapter",
19401
+ connectionHosts: "Hosts",
19402
+ connectionLocalHost: "Local",
19403
+ connectionRemoteHost: "Remote Host",
19404
+ connectionActiveHost: "Current",
19125
19405
  connectionReason: "Reason",
19126
19406
  connectionRefresh: "Run connection diagnostics",
19127
19407
  connectionRefreshing: "Running diagnostics...",
@@ -19190,6 +19470,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19190
19470
  runtimeCapabilityNotInstalled: "\u8FD0\u884C\u65F6\u5C1A\u672A\u5B89\u88C5\u8BE5\u9879\u80FD\u529B\uFF0C\u56E0\u6B64\u6682\u4E0D\u53EF\u7528\u3002",
19191
19471
  connectionsDescription: "\u67E5\u770B\u9002\u914D\u5668\u548C Agent \u8FD0\u884C\u65F6\u7684\u771F\u5B9E\u68C0\u67E5\u7ED3\u679C\u3002\u5931\u8D25\u68C0\u67E5\u4F1A\u4FDD\u7559\u9519\u8BEF\u8BE6\u60C5\uFF0C\u65B9\u4FBF\u6392\u67E5\u65E0\u6CD5\u9009\u62E9\u7684\u95EE\u9898\u3002",
19192
19472
  connectionAdapter: "Renderer \u9002\u914D\u5668",
19473
+ connectionHosts: "Host \u5217\u8868",
19474
+ connectionLocalHost: "\u672C\u5730",
19475
+ connectionRemoteHost: "\u8FDC\u7A0B Host",
19476
+ connectionActiveHost: "\u5F53\u524D",
19193
19477
  connectionReason: "\u539F\u56E0",
19194
19478
  connectionRefresh: "\u91CD\u65B0\u8BCA\u65AD\u8FDE\u63A5",
19195
19479
  connectionRefreshing: "\u6B63\u5728\u8BCA\u65AD...",
@@ -19833,10 +20117,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19833
20117
  if (availability === "checking" || availability === "installing") return "checking";
19834
20118
  return "failed";
19835
20119
  }
19836
- function diagnosticText(name, snapshot) {
20120
+ function diagnosticText(hostId, name, snapshot) {
19837
20121
  const error51 = snapshot.error;
19838
20122
  const lines = [
19839
20123
  "codexhost connection diagnostics",
20124
+ `host: ${hostId}`,
19840
20125
  `agent: ${name}`,
19841
20126
  `status: ${snapshot.availability}`,
19842
20127
  ...error51 ? [
@@ -19882,7 +20167,7 @@ ${error51.stderrTail}`] : []
19882
20167
  () => showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel)
19883
20168
  );
19884
20169
  }
19885
- function appendConnectionRow(document2, parent, name, availability, detail, error51, messages, rowAttribute, diagnosticSnapshot) {
20170
+ function appendConnectionRow(document2, parent, name, availability, detail, error51, messages, rowAttribute, diagnosticSnapshot, diagnosticHostId) {
19886
20171
  const row = document2.createElement("div");
19887
20172
  row.className = "settings-connection-row";
19888
20173
  if (rowAttribute) row.dataset.connectionAgent = rowAttribute;
@@ -19945,7 +20230,7 @@ ${error51.stderrTail}`] : []
19945
20230
  copyDiagnosticsToClipboard(
19946
20231
  document2,
19947
20232
  copy,
19948
- diagnosticText(name, diagnosticSnapshot),
20233
+ diagnosticText(diagnosticHostId ?? "unknown", name, diagnosticSnapshot),
19949
20234
  messages,
19950
20235
  messages.connectionCopyDetails
19951
20236
  );
@@ -19961,6 +20246,18 @@ ${error51.stderrTail}`] : []
19961
20246
  row.append(identity, status, detailElement, details);
19962
20247
  parent.append(row);
19963
20248
  }
20249
+ function connectionHostLabel(hostId, active, messages) {
20250
+ const activeLabel = active ? ` \xB7 ${messages.connectionActiveHost}` : "";
20251
+ if (hostId === "local") return `${messages.connectionLocalHost}${activeLabel}`;
20252
+ const separator = hostId.lastIndexOf(":");
20253
+ const encodedName = separator >= 0 ? hostId.slice(separator + 1) : hostId;
20254
+ let name = encodedName;
20255
+ try {
20256
+ name = decodeURIComponent(encodedName);
20257
+ } catch {
20258
+ }
20259
+ return `${messages.connectionRemoteHost}: ${name}${activeLabel}`;
20260
+ }
19964
20261
  function connectionsPage(messages, getDiagnostics) {
19965
20262
  return Object.freeze({
19966
20263
  id: "connections",
@@ -19991,7 +20288,10 @@ ${error51.stderrTail}`] : []
19991
20288
  content.className = "settings-connection-list";
19992
20289
  context.content.append(heading, description, actions, content);
19993
20290
  let pending = false;
20291
+ let selectedHostId = "local";
20292
+ let latestSnapshot = null;
19994
20293
  const render = (snapshot) => {
20294
+ latestSnapshot = snapshot;
19995
20295
  content.replaceChildren();
19996
20296
  if (!snapshot) {
19997
20297
  const empty = document2.createElement("div");
@@ -20010,19 +20310,50 @@ ${error51.stderrTail}`] : []
20010
20310
  messages,
20011
20311
  "renderer-adapter"
20012
20312
  );
20013
- for (const agent of snapshot.agents) {
20313
+ const selectedHost = snapshot.hosts.find((host) => host.hostId === selectedHostId) ?? snapshot.hosts.find((host) => host.hostId === "local") ?? snapshot.hosts[0];
20314
+ if (!selectedHost) return;
20315
+ selectedHostId = selectedHost.hostId;
20316
+ const tabs = document2.createElement("div");
20317
+ tabs.className = "settings-connection-host-tabs";
20318
+ tabs.setAttribute("role", "tablist");
20319
+ tabs.setAttribute("aria-label", messages.connectionHosts);
20320
+ const hostSection = document2.createElement("section");
20321
+ hostSection.className = "settings-connection-host";
20322
+ hostSection.dataset.connectionHost = selectedHost.hostId;
20323
+ hostSection.setAttribute("role", "tabpanel");
20324
+ const panelId = "codexhost-settings-connection-host-panel";
20325
+ hostSection.id = panelId;
20326
+ for (const host of snapshot.hosts) {
20327
+ const tab = document2.createElement("button");
20328
+ tab.type = "button";
20329
+ tab.className = "settings-connection-host-tab";
20330
+ tab.dataset.connectionHostTab = host.hostId;
20331
+ tab.setAttribute("role", "tab");
20332
+ tab.setAttribute("aria-controls", panelId);
20333
+ tab.setAttribute("aria-selected", String(host.hostId === selectedHost.hostId));
20334
+ tab.tabIndex = host.hostId === selectedHost.hostId ? 0 : -1;
20335
+ tab.textContent = connectionHostLabel(host.hostId, host.active, messages);
20336
+ tab.addEventListener("click", () => {
20337
+ selectedHostId = host.hostId;
20338
+ render(latestSnapshot);
20339
+ });
20340
+ tabs.append(tab);
20341
+ }
20342
+ for (const agent of selectedHost.agents) {
20014
20343
  appendConnectionRow(
20015
20344
  document2,
20016
- content,
20345
+ hostSection,
20017
20346
  RENDERER_AGENT_LABELS[agent.agent],
20018
20347
  agent.availability,
20019
20348
  null,
20020
20349
  agent.error,
20021
20350
  messages,
20022
20351
  agent.agent,
20023
- agent
20352
+ agent,
20353
+ selectedHost.hostId
20024
20354
  );
20025
20355
  }
20356
+ content.append(tabs, hostSection);
20026
20357
  };
20027
20358
  const diagnostics = getDiagnostics();
20028
20359
  render(diagnostics?.snapshot() ?? null);
@@ -20032,7 +20363,11 @@ ${error51.stderrTail}`] : []
20032
20363
  }
20033
20364
  copyAll.addEventListener("click", () => {
20034
20365
  const snapshot = diagnostics.snapshot();
20035
- const report = snapshot.agents.map((agent) => diagnosticText(RENDERER_AGENT_LABELS[agent.agent], agent)).join("\n\n");
20366
+ const report = snapshot.hosts.flatMap(
20367
+ (host) => host.agents.map(
20368
+ (agent) => diagnosticText(host.hostId, RENDERER_AGENT_LABELS[agent.agent], agent)
20369
+ )
20370
+ ).join("\n\n");
20036
20371
  copyDiagnosticsToClipboard(document2, copyAll, report, messages, messages.connectionCopyAll);
20037
20372
  });
20038
20373
  const unsubscribe = diagnostics.subscribe(() => render(diagnostics.snapshot()));
@@ -20061,17 +20396,21 @@ ${error51.stderrTail}`] : []
20061
20396
  createRendererSettingsIcon("diagnose", 16),
20062
20397
  messages.connectionRefresh
20063
20398
  );
20399
+ const snapshot = diagnostics.snapshot();
20064
20400
  render({
20065
- ...diagnostics.snapshot(),
20066
- agents: diagnostics.snapshot().agents.map((agent) => ({
20067
- ...agent,
20068
- availability: "error",
20069
- error: {
20070
- code: "internalError",
20071
- message: error51 instanceof Error ? error51.message : String(error51),
20072
- retryable: true,
20073
- stage: "request"
20074
- }
20401
+ ...snapshot,
20402
+ hosts: snapshot.hosts.map((host) => ({
20403
+ ...host,
20404
+ agents: host.agents.map((agent) => ({
20405
+ ...agent,
20406
+ availability: "error",
20407
+ error: {
20408
+ code: "internalError",
20409
+ message: error51 instanceof Error ? error51.message : String(error51),
20410
+ retryable: true,
20411
+ stage: "request"
20412
+ }
20413
+ }))
20075
20414
  }))
20076
20415
  });
20077
20416
  }
@@ -20371,7 +20710,7 @@ ${error51.stderrTail}`] : []
20371
20710
  }
20372
20711
 
20373
20712
  // src/settings/shell.css
20374
- var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-header-actions {\n display: flex;\n align-items: center;\n flex: none;\n gap: 12px;\n margin-left: auto;\n}\n\n.settings-icon-button.settings-star-link {\n width: auto;\n gap: 6px;\n padding-inline: 8px;\n font-size: 12px;\n line-height: 18px;\n text-decoration: none;\n border: 2px dashed #f5c542;\n white-space: nowrap;\n}\n\n.settings-star-link .codexhost-settings-icon {\n color: #f5c542;\n fill: currentColor;\n stroke: currentColor;\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-page-description {\n max-width: 760px;\n margin: -12px 0 24px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-connection-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.settings-connection-list {\n min-width: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row {\n display: grid;\n grid-template-columns: minmax(180px, 1fr) auto minmax(260px, 1.5fr);\n align-items: center;\n min-width: 0;\n min-height: 76px;\n gap: 24px;\n padding: 14px 0;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n gap: 10px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 20px;\n}\n\n.settings-connection-row__identity strong {\n min-width: 0;\n overflow-wrap: anywhere;\n font-weight: 500;\n}\n\n.settings-connection-row__dot {\n width: 9px;\n height: 9px;\n flex: none;\n border-radius: 50%;\n background: var(--settings-muted);\n}\n\n.settings-connection-row__dot[data-connection-tone="ready"] {\n background: #22c55e;\n}\n\n.settings-connection-row__dot[data-connection-tone="checking"] {\n background: #f5c542;\n}\n\n.settings-connection-row__dot[data-connection-tone="failed"] {\n background: #ef4444;\n}\n\n.settings-status-badge[data-connection-tone="ready"] {\n color: #4ade80;\n}\n\n.settings-status-badge[data-connection-tone="checking"] {\n color: #f5c542;\n}\n\n.settings-status-badge[data-connection-tone="failed"] {\n color: #f87171;\n}\n\n.settings-connection-row__detail {\n display: grid;\n min-width: 0;\n align-items: center;\n gap: 2px 12px;\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-row__reason {\n min-width: 0;\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n.settings-connection-details-toggle {\n width: fit-content;\n padding: 0;\n color: var(--settings-focus);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n background: transparent;\n border: 0;\n cursor: pointer;\n}\n\n.settings-connection-details-toggle:hover {\n text-decoration: underline;\n}\n\n.settings-connection-details {\n display: grid;\n grid-column: 1 / -1;\n gap: 6px;\n min-width: 0;\n margin: 4px 0 2px 32px;\n padding: 12px;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 4%);\n border-left: 2px solid #ef4444;\n border-radius: 4px;\n}\n\n.settings-connection-detail-line {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n gap: 12px;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-detail-line span {\n color: var(--settings-muted);\n}\n\n.settings-connection-detail-line code {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n white-space: pre-wrap;\n}\n\n.settings-connection-stderr {\n max-height: 220px;\n min-width: 0;\n margin: 4px 0 0;\n padding: 10px;\n overflow: auto;\n color: #fca5a5;\n font:\n 12px/18px ui-monospace,\n "SFMono-Regular",\n Consolas,\n "Liberation Mono",\n monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(0 0 0 / 28%);\n border-radius: 4px;\n}\n\n.settings-connection-details .settings-command-button {\n justify-self: start;\n min-height: 30px;\n font-size: 12px;\n}\n\n.settings-connection-row__detail time {\n color: var(--settings-muted);\n opacity: 0.8;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n display: grid;\n min-width: 0;\n gap: 12px;\n color: var(--settings-muted);\n font-size: 14px;\n line-height: 22px;\n overflow-wrap: anywhere;\n}\n\n.settings-update-notes > :first-child {\n margin-top: 0;\n}\n\n.settings-update-notes > :last-child {\n margin-bottom: 0;\n}\n\n.settings-update-notes h1,\n.settings-update-notes h2,\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n margin: 6px 0 0;\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes h1 {\n font-size: 18px;\n line-height: 26px;\n}\n\n.settings-update-notes h2 {\n font-size: 17px;\n line-height: 24px;\n}\n\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n font-size: 14px;\n line-height: 22px;\n}\n\n.settings-update-notes p,\n.settings-update-notes ul,\n.settings-update-notes ol,\n.settings-update-notes pre,\n.settings-update-notes blockquote {\n margin: 0;\n}\n\n.settings-update-notes ul,\n.settings-update-notes ol {\n padding-left: 24px;\n}\n\n.settings-update-notes li {\n padding-left: 4px;\n}\n\n.settings-update-notes li::marker {\n color: var(--settings-muted);\n font-size: 0.85em;\n}\n\n.settings-update-notes li + li {\n margin-top: 14px;\n}\n\n.settings-update-notes .release-note-translation {\n display: block;\n margin-top: 3px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-notes blockquote {\n padding: 8px 12px;\n color: var(--settings-muted);\n border-left: 3px solid var(--settings-focus);\n background: rgb(255 255 255 / 4%);\n}\n\n.settings-update-notes hr {\n width: 100%;\n margin: 2px 0;\n border: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-update-notes strong {\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes a {\n color: #7cb8ff;\n text-decoration: none;\n}\n\n.settings-update-notes a:hover {\n text-decoration: underline;\n}\n\n.settings-update-notes code {\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n}\n\n.settings-update-notes :not(pre) > code {\n padding: 1px 5px;\n background: rgb(255 255 255 / 8%);\n border-radius: 4px;\n}\n\n.settings-update-notes pre {\n min-width: 0;\n max-width: 100%;\n padding: 10px 12px;\n overflow-x: auto;\n overflow-y: hidden;\n background: rgb(0 0 0 / 28%);\n border-radius: 6px;\n}\n\n.settings-update-notes pre code {\n line-height: 18px;\n white-space: pre-wrap;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-manual {\n display: grid;\n min-width: 0;\n gap: 6px;\n margin-top: 14px;\n padding-inline: 4px;\n}\n\n.settings-update-manual[hidden] {\n display: none;\n}\n\n.settings-update-manual span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-manual code {\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-icon-button.settings-star-link {\n width: 28px;\n padding: 0;\n }\n\n .settings-star-link span {\n display: none;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-connection-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 10px 16px;\n }\n\n .settings-connection-row__detail {\n grid-column: 1 / -1;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
20713
+ var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-header-actions {\n display: flex;\n align-items: center;\n flex: none;\n gap: 12px;\n margin-left: auto;\n}\n\n.settings-icon-button.settings-star-link {\n width: auto;\n gap: 6px;\n padding-inline: 8px;\n font-size: 12px;\n line-height: 18px;\n text-decoration: none;\n border: 2px dashed #f5c542;\n white-space: nowrap;\n}\n\n.settings-star-link .codexhost-settings-icon {\n color: #f5c542;\n fill: currentColor;\n stroke: currentColor;\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-page-description {\n max-width: 760px;\n margin: -12px 0 24px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-connection-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.settings-connection-list {\n min-width: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-connection-host-tabs {\n display: flex;\n gap: 6px;\n padding: 20px 0 8px;\n overflow-x: auto;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-host-tab {\n flex: none;\n padding: 7px 12px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--settings-muted);\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n}\n\n.settings-connection-host-tab:hover {\n background: var(--settings-hover);\n color: var(--settings-text);\n}\n\n.settings-connection-host-tab[aria-selected="true"] {\n background: var(--settings-active);\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-connection-host-tab:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: -2px;\n}\n\n.settings-connection-row {\n display: grid;\n grid-template-columns: minmax(180px, 1fr) auto minmax(260px, 1.5fr);\n align-items: center;\n min-width: 0;\n min-height: 76px;\n gap: 24px;\n padding: 14px 0;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n gap: 10px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 20px;\n}\n\n.settings-connection-row__identity strong {\n min-width: 0;\n overflow-wrap: anywhere;\n font-weight: 500;\n}\n\n.settings-connection-row__dot {\n width: 9px;\n height: 9px;\n flex: none;\n border-radius: 50%;\n background: var(--settings-muted);\n}\n\n.settings-connection-row__dot[data-connection-tone="ready"] {\n background: #22c55e;\n}\n\n.settings-connection-row__dot[data-connection-tone="checking"] {\n background: #f5c542;\n}\n\n.settings-connection-row__dot[data-connection-tone="failed"] {\n background: #ef4444;\n}\n\n.settings-status-badge[data-connection-tone="ready"] {\n color: #4ade80;\n}\n\n.settings-status-badge[data-connection-tone="checking"] {\n color: #f5c542;\n}\n\n.settings-status-badge[data-connection-tone="failed"] {\n color: #f87171;\n}\n\n.settings-connection-row__detail {\n display: grid;\n min-width: 0;\n align-items: center;\n gap: 2px 12px;\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-row__reason {\n min-width: 0;\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n.settings-connection-details-toggle {\n width: fit-content;\n padding: 0;\n color: var(--settings-focus);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n background: transparent;\n border: 0;\n cursor: pointer;\n}\n\n.settings-connection-details-toggle:hover {\n text-decoration: underline;\n}\n\n.settings-connection-details {\n display: grid;\n grid-column: 1 / -1;\n gap: 6px;\n min-width: 0;\n margin: 4px 0 2px 32px;\n padding: 12px;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 4%);\n border-left: 2px solid #ef4444;\n border-radius: 4px;\n}\n\n.settings-connection-detail-line {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n gap: 12px;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-detail-line span {\n color: var(--settings-muted);\n}\n\n.settings-connection-detail-line code {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n white-space: pre-wrap;\n}\n\n.settings-connection-stderr {\n max-height: 220px;\n min-width: 0;\n margin: 4px 0 0;\n padding: 10px;\n overflow: auto;\n color: #fca5a5;\n font:\n 12px/18px ui-monospace,\n "SFMono-Regular",\n Consolas,\n "Liberation Mono",\n monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(0 0 0 / 28%);\n border-radius: 4px;\n}\n\n.settings-connection-details .settings-command-button {\n justify-self: start;\n min-height: 30px;\n font-size: 12px;\n}\n\n.settings-connection-row__detail time {\n color: var(--settings-muted);\n opacity: 0.8;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n display: grid;\n min-width: 0;\n gap: 12px;\n color: var(--settings-muted);\n font-size: 14px;\n line-height: 22px;\n overflow-wrap: anywhere;\n}\n\n.settings-update-notes > :first-child {\n margin-top: 0;\n}\n\n.settings-update-notes > :last-child {\n margin-bottom: 0;\n}\n\n.settings-update-notes h1,\n.settings-update-notes h2,\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n margin: 6px 0 0;\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes h1 {\n font-size: 18px;\n line-height: 26px;\n}\n\n.settings-update-notes h2 {\n font-size: 17px;\n line-height: 24px;\n}\n\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n font-size: 14px;\n line-height: 22px;\n}\n\n.settings-update-notes p,\n.settings-update-notes ul,\n.settings-update-notes ol,\n.settings-update-notes pre,\n.settings-update-notes blockquote {\n margin: 0;\n}\n\n.settings-update-notes ul,\n.settings-update-notes ol {\n padding-left: 24px;\n}\n\n.settings-update-notes li {\n padding-left: 4px;\n}\n\n.settings-update-notes li::marker {\n color: var(--settings-muted);\n font-size: 0.85em;\n}\n\n.settings-update-notes li + li {\n margin-top: 14px;\n}\n\n.settings-update-notes .release-note-translation {\n display: block;\n margin-top: 3px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-notes blockquote {\n padding: 8px 12px;\n color: var(--settings-muted);\n border-left: 3px solid var(--settings-focus);\n background: rgb(255 255 255 / 4%);\n}\n\n.settings-update-notes hr {\n width: 100%;\n margin: 2px 0;\n border: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-update-notes strong {\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes a {\n color: #7cb8ff;\n text-decoration: none;\n}\n\n.settings-update-notes a:hover {\n text-decoration: underline;\n}\n\n.settings-update-notes code {\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n}\n\n.settings-update-notes :not(pre) > code {\n padding: 1px 5px;\n background: rgb(255 255 255 / 8%);\n border-radius: 4px;\n}\n\n.settings-update-notes pre {\n min-width: 0;\n max-width: 100%;\n padding: 10px 12px;\n overflow-x: auto;\n overflow-y: hidden;\n background: rgb(0 0 0 / 28%);\n border-radius: 6px;\n}\n\n.settings-update-notes pre code {\n line-height: 18px;\n white-space: pre-wrap;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-manual {\n display: grid;\n min-width: 0;\n gap: 6px;\n margin-top: 14px;\n padding-inline: 4px;\n}\n\n.settings-update-manual[hidden] {\n display: none;\n}\n\n.settings-update-manual span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-manual code {\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-icon-button.settings-star-link {\n width: 28px;\n padding: 0;\n }\n\n .settings-star-link span {\n display: none;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-connection-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 10px 16px;\n }\n\n .settings-connection-row__detail {\n grid-column: 1 / -1;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
20375
20714
 
20376
20715
  // src/settings/shell.ts
20377
20716
  var SETTINGS_SHELL_ATTRIBUTE = "data-codexhost-settings-shell";
@@ -21034,6 +21373,9 @@ ${error51.stderrTail}`] : []
21034
21373
  function draftPermissionMode(catalog, requested) {
21035
21374
  return catalog.modes.find(({ id }) => id === requested)?.id ?? catalog.modes.find(({ id }) => id === catalog.defaultModeId)?.id ?? catalog.defaultModeId;
21036
21375
  }
21376
+ function shouldPersistNewThreadConfigurationSelection(phase) {
21377
+ return phase === "draft";
21378
+ }
21037
21379
  function restoredThreadOwnership(inspection) {
21038
21380
  if (inspection.owner === "codex") return { agent: "codex" };
21039
21381
  if (inspection.harnessId === "pi") {
@@ -21057,10 +21399,12 @@ ${error51.stderrTail}`] : []
21057
21399
  }
21058
21400
  const model = inspection.effectiveModel ?? transportSelection.model;
21059
21401
  const thinkingOptionId = selectableThinkingOptionId(inspection) ?? transportSelection.thinkingOptionId;
21402
+ const permissionModeId = inspection.effectivePermissionModeId ?? transportSelection.permissionModeId;
21060
21403
  return {
21061
21404
  agent: "grok",
21062
21405
  ...model ? { model } : {},
21063
- ...thinkingOptionId ? { thinkingOptionId } : {}
21406
+ ...thinkingOptionId ? { thinkingOptionId } : {},
21407
+ ...permissionModeId ? { permissionModeId } : {}
21064
21408
  };
21065
21409
  }
21066
21410
  if (inspection.harnessId === "claude-code") {
@@ -21091,10 +21435,10 @@ ${error51.stderrTail}`] : []
21091
21435
  function isOwnershipSubmissionBlocked(status) {
21092
21436
  return status === "loading" || status === "error";
21093
21437
  }
21094
- function shouldTransferComposerState(sourceTarget, replacementTarget, sourcePhase) {
21438
+ function shouldTransferComposerState(sourceTarget, replacementTarget, sourcePhase, submissionPending = false) {
21095
21439
  if (!sourceTarget || !replacementTarget) return false;
21096
21440
  if (sourceTarget === replacementTarget) return true;
21097
- return sourcePhase === "locked" && sourceTarget[0] === "default" && replacementTarget[0] === "conversation";
21441
+ return (sourcePhase === "locked" || submissionPending) && sourceTarget[0] === "default" && replacementTarget[0] === "conversation";
21098
21442
  }
21099
21443
  function isLateConversationTarget(mountedTarget, currentTarget) {
21100
21444
  if (currentTarget?.[0] !== "conversation") return false;
@@ -21103,13 +21447,16 @@ ${error51.stderrTail}`] : []
21103
21447
  if (mountedTarget?.[0] !== "conversation") return false;
21104
21448
  return mountedTarget.length !== currentTarget.length || mountedTarget.some((value, index) => value !== currentTarget[index]);
21105
21449
  }
21106
- function lateConversationTargetResolution(mountedTarget, currentTarget, sourcePhase) {
21450
+ function lateConversationTargetResolution(mountedTarget, currentTarget, sourcePhase, submissionPending = false) {
21107
21451
  if (!isLateConversationTarget(mountedTarget, currentTarget)) return "none";
21108
- return mountedTarget?.[0] === "default" && sourcePhase === "locked" ? "transfer" : "inspect";
21452
+ return mountedTarget?.[0] === "default" && (sourcePhase === "locked" || submissionPending) ? "transfer" : "inspect";
21109
21453
  }
21110
21454
  function isComposerModelWriteAllowed(target) {
21111
21455
  return target?.[0] === "default";
21112
21456
  }
21457
+ function shouldApplyDraftAgentCarrier(agent, model) {
21458
+ return agent === "codex" || model !== void 0;
21459
+ }
21113
21460
  function applyComposerModelWrite(target, write) {
21114
21461
  if (target?.[0] === "conversation") return true;
21115
21462
  if (!isComposerModelWriteAllowed(target)) return false;
@@ -21159,6 +21506,8 @@ ${error51.stderrTail}`] : []
21159
21506
  let modelControl = null;
21160
21507
  let usageNotificationDispose = null;
21161
21508
  const localAgentForSidebarThread = (input) => {
21509
+ const mountedHostId = modelControl?.currentHostId?.() ?? "local";
21510
+ if (input.hostId !== mountedHostId) return null;
21162
21511
  for (const mounted of mountedByComposer.values()) {
21163
21512
  const target = mounted.modelTarget;
21164
21513
  if (target?.[0] === "default" && input.draftId !== null && target[1] === input.draftId) {
@@ -21171,7 +21520,7 @@ ${error51.stderrTail}`] : []
21171
21520
  return null;
21172
21521
  };
21173
21522
  const sidebarAgentIcons = installRendererSidebarAgentIcons({
21174
- getClient: () => modelControl,
21523
+ getClient: (hostId) => modelClientForHost(hostId),
21175
21524
  getLocalAgent: localAgentForSidebarThread
21176
21525
  });
21177
21526
  let connectionDiagnostics = null;
@@ -21185,23 +21534,36 @@ ${error51.stderrTail}`] : []
21185
21534
  modelUpdates: 0,
21186
21535
  hook: null
21187
21536
  };
21188
- let harnessAvailability = Object.fromEntries(
21189
- externalAgents.map((agent) => [agent, "checking"])
21190
- );
21191
- const harnessAvailabilityErrors = {
21192
- pi: void 0,
21193
- "claude-code": void 0,
21194
- "deepseek-harness": void 0,
21195
- grok: void 0
21537
+ const createHostHarnessAvailabilityState = () => ({
21538
+ availability: Object.fromEntries(
21539
+ externalAgents.map((agent) => [agent, "checking"])
21540
+ ),
21541
+ errors: {
21542
+ pi: void 0,
21543
+ "claude-code": void 0,
21544
+ "deepseek-harness": void 0,
21545
+ grok: void 0
21546
+ },
21547
+ requestGeneration: 0,
21548
+ request: null,
21549
+ retryTimer: null,
21550
+ retryAttempt: 0
21551
+ });
21552
+ const harnessAvailabilityByHost = /* @__PURE__ */ new Map();
21553
+ const hostHarnessAvailabilityState = (hostId) => {
21554
+ let state = harnessAvailabilityByHost.get(hostId);
21555
+ if (!state) {
21556
+ state = createHostHarnessAvailabilityState();
21557
+ harnessAvailabilityByHost.set(hostId, state);
21558
+ }
21559
+ return state;
21196
21560
  };
21561
+ let activeAvailabilityHostId = "local";
21562
+ const activeHarnessAvailabilityState = () => hostHarnessAvailabilityState(activeAvailabilityHostId);
21197
21563
  const connectionListeners = /* @__PURE__ */ new Set();
21198
21564
  const publishConnectionStatus = () => {
21199
21565
  for (const listener of connectionListeners) listener();
21200
21566
  };
21201
- let availabilityRequestGeneration = 0;
21202
- let availabilityRequest = null;
21203
- let availabilityRetryTimer = null;
21204
- let availabilityRetryAttempt = 0;
21205
21567
  const availabilityRetryDelays = [500, 1e3, 2e3, 4e3, 8e3];
21206
21568
  const usageRefreshTimers = /* @__PURE__ */ new Map();
21207
21569
  const usageRefreshAttempts = /* @__PURE__ */ new Map();
@@ -21217,7 +21579,8 @@ ${error51.stderrTail}`] : []
21217
21579
  writeNewThreadExternalConfigurationPreference(
21218
21580
  state.agent,
21219
21581
  model,
21220
- controller.thinkingOptionForAgent(composer, state.agent)
21582
+ controller.thinkingOptionForAgent(composer, state.agent),
21583
+ controller.permissionModeForAgent(composer, state.agent)
21221
21584
  );
21222
21585
  }
21223
21586
  }
@@ -21237,7 +21600,7 @@ ${error51.stderrTail}`] : []
21237
21600
  controller.get(mounted.composer),
21238
21601
  adapterStatus.state,
21239
21602
  controller.isSwitching(mounted.composer) || isOwnershipSubmissionBlocked(mounted.ownershipStatus),
21240
- harnessAvailability,
21603
+ activeHarnessAvailabilityState().availability,
21241
21604
  mounted.modelView,
21242
21605
  mounted.permissionModeView,
21243
21606
  mounted.usage,
@@ -21426,7 +21789,8 @@ ${error51.stderrTail}`] : []
21426
21789
  const resolution = lateConversationTargetResolution(
21427
21790
  mounted.modelTarget,
21428
21791
  currentTarget,
21429
- controller.get(mounted.composer).phase
21792
+ controller.get(mounted.composer).phase,
21793
+ controller.isSubmissionPending(mounted.composer)
21430
21794
  );
21431
21795
  if (resolution === "none") return false;
21432
21796
  const previousTarget = mounted.modelTarget;
@@ -21463,7 +21827,7 @@ ${error51.stderrTail}`] : []
21463
21827
  const state = controller.get(mounted.composer);
21464
21828
  if (state.agent === "codex") return;
21465
21829
  const agent = state.agent;
21466
- const availability = harnessAvailability[agent];
21830
+ const availability = activeHarnessAvailabilityState().availability[agent];
21467
21831
  if (availability !== "ready") {
21468
21832
  mounted.modelView = {
21469
21833
  status: adapterStatus.state !== "ready" || availability === "checking" ? "waitingForAdapter" : "error",
@@ -21493,6 +21857,16 @@ ${error51.stderrTail}`] : []
21493
21857
  }
21494
21858
  if (inspection.status !== "ready") throw new Error(inspection.error.message);
21495
21859
  const current = controller.get(mounted.composer);
21860
+ const previousModel = controller.modelForAgent(mounted.composer, agent);
21861
+ const previousModelAvailable = previousModel !== void 0 && inspection.catalog.models.some((model) => model.ref.id === previousModel.id);
21862
+ if (current.phase === "locked" && previousModel && !previousModelAvailable) {
21863
+ throw new Error("Existing Thread Model is absent from the current Catalog");
21864
+ }
21865
+ const preferredConfiguration = current.phase === "draft" && !previousModelAvailable ? readNewThreadExternalConfigurationPreference(
21866
+ agent,
21867
+ inspection.catalog,
21868
+ inspection.permissionModes
21869
+ ) : void 0;
21496
21870
  const previousPermissionModeId = controller.permissionModeForAgent(mounted.composer, agent);
21497
21871
  let selectedPermissionModeId;
21498
21872
  if (inspection.capabilities.configuration.selectPermissionMode) {
@@ -21502,7 +21876,7 @@ ${error51.stderrTail}`] : []
21502
21876
  }
21503
21877
  mounted.permissionModeView = { status: "loading", catalog: permissionModes };
21504
21878
  const effectivePermissionModeId = current.phase === "locked" ? mounted.threadConfiguration?.effectivePermissionModeId : void 0;
21505
- const preferredPermissionModeId = agent === "claude-code" ? readClaudePermissionModePreference(permissionModes) : void 0;
21879
+ const preferredPermissionModeId = preferredConfiguration?.permissionModeId ?? (agent === "claude-code" ? readClaudePermissionModePreference(permissionModes) : void 0);
21506
21880
  selectedPermissionModeId = draftPermissionMode(
21507
21881
  permissionModes,
21508
21882
  effectivePermissionModeId ?? previousPermissionModeId ?? preferredPermissionModeId
@@ -21531,12 +21905,6 @@ ${error51.stderrTail}`] : []
21531
21905
  }
21532
21906
  return;
21533
21907
  }
21534
- const previousModel = controller.modelForAgent(mounted.composer, agent);
21535
- const previousModelAvailable = previousModel !== void 0 && inspection.catalog.models.some((model) => model.ref.id === previousModel.id);
21536
- if (current.phase === "locked" && previousModel && !previousModelAvailable) {
21537
- throw new Error("Existing Thread Model is absent from the current Catalog");
21538
- }
21539
- const preferredConfiguration = current.phase === "draft" && !previousModelAvailable ? readNewThreadExternalConfigurationPreference(agent, inspection.catalog) : void 0;
21540
21908
  const selected = previousModelAvailable ? previousModel : preferredConfiguration?.model ?? inspection.catalog.defaultModel;
21541
21909
  if (!selected) throw new Error("External Harness did not report its default Model");
21542
21910
  const effectiveCatalog = current.phase === "locked" && mounted.threadConfiguration ? catalogWithConfigurationState(inspection.catalog, selected, mounted.threadConfiguration) : inspection.catalog;
@@ -21616,6 +21984,7 @@ ${error51.stderrTail}`] : []
21616
21984
  }
21617
21985
  };
21618
21986
  const selectExternalModel = async (mounted, modelId) => {
21987
+ controller.clearPendingSubmission(mounted.composer);
21619
21988
  const current = controller.get(mounted.composer);
21620
21989
  if (current.agent === "codex") return;
21621
21990
  const agent = current.agent;
@@ -21687,13 +22056,13 @@ ${error51.stderrTail}`] : []
21687
22056
  effectiveThinkingOptionId = supportsThinkingSelection ? selectableThinkingOptionId(state) : void 0;
21688
22057
  effectiveCatalog = supportsThinkingSelection ? catalogWithConfigurationState(catalog, effectiveModel, state) : catalog;
21689
22058
  resolvedModelLabel = state.resolvedModelLabel;
21690
- const effectivePermissionModeId = state.effectivePermissionModeId ?? previousPermissionModeId;
22059
+ const effectivePermissionModeId2 = state.effectivePermissionModeId ?? previousPermissionModeId;
21691
22060
  if (!applyExternalConfiguration(
21692
22061
  mounted,
21693
22062
  agent,
21694
22063
  effectiveModel,
21695
22064
  effectiveThinkingOptionId,
21696
- effectivePermissionModeId
22065
+ effectivePermissionModeId2
21697
22066
  )) {
21698
22067
  throw new Error("Confirmed external Model could not be applied to the Composer");
21699
22068
  }
@@ -21702,11 +22071,18 @@ ${error51.stderrTail}`] : []
21702
22071
  if (!isCurrentModelRequest(mounted, generation)) return;
21703
22072
  controller.setExternalModel(mounted.composer, agent, effectiveModel);
21704
22073
  controller.setExternalThinkingOption(mounted.composer, agent, effectiveThinkingOptionId);
21705
- writeNewThreadExternalConfigurationPreference(
21706
- agent,
21707
- effectiveModel,
21708
- effectiveThinkingOptionId
21709
- );
22074
+ const effectivePermissionModeId = mounted.threadConfiguration?.effectivePermissionModeId ?? previousPermissionModeId;
22075
+ if (effectivePermissionModeId) {
22076
+ controller.setExternalPermissionMode(mounted.composer, agent, effectivePermissionModeId);
22077
+ }
22078
+ if (shouldPersistNewThreadConfigurationSelection(current.phase)) {
22079
+ writeNewThreadExternalConfigurationPreference(
22080
+ agent,
22081
+ effectiveModel,
22082
+ effectiveThinkingOptionId,
22083
+ effectivePermissionModeId
22084
+ );
22085
+ }
21710
22086
  mounted.modelView = {
21711
22087
  status: "ready",
21712
22088
  catalog: effectiveCatalog,
@@ -21739,6 +22115,7 @@ ${error51.stderrTail}`] : []
21739
22115
  }
21740
22116
  };
21741
22117
  const selectPermissionMode = async (mounted, permissionModeId) => {
22118
+ controller.clearPendingSubmission(mounted.composer);
21742
22119
  const current = controller.get(mounted.composer);
21743
22120
  if (current.agent === "codex") return;
21744
22121
  const agent = current.agent;
@@ -21747,9 +22124,6 @@ ${error51.stderrTail}`] : []
21747
22124
  const model = controller.modelForAgent(mounted.composer, agent);
21748
22125
  if (!catalog || !selectedPermissionModeId || !model || !modelControl) return;
21749
22126
  const previousPermissionModeId = controller.permissionModeForAgent(mounted.composer, agent);
21750
- if (agent === "claude-code") {
21751
- writeClaudePermissionModePreference(selectedPermissionModeId);
21752
- }
21753
22127
  const thinkingOptionId = controller.thinkingOptionForAgent(mounted.composer, agent);
21754
22128
  const generation = controller.beginModelRequest(mounted.composer);
21755
22129
  mounted.permissionModeView = {
@@ -21814,6 +22188,17 @@ ${error51.stderrTail}`] : []
21814
22188
  }
21815
22189
  if (!isCurrentModelRequest(mounted, generation)) return;
21816
22190
  controller.setExternalPermissionMode(mounted.composer, agent, effectivePermissionModeId);
22191
+ if (shouldPersistNewThreadConfigurationSelection(current.phase)) {
22192
+ writeNewThreadExternalConfigurationPreference(
22193
+ agent,
22194
+ model,
22195
+ thinkingOptionId,
22196
+ effectivePermissionModeId
22197
+ );
22198
+ if (agent === "claude-code") {
22199
+ writeClaudePermissionModePreference(effectivePermissionModeId);
22200
+ }
22201
+ }
21817
22202
  mounted.permissionModeView = {
21818
22203
  status: "ready",
21819
22204
  catalog,
@@ -21841,6 +22226,7 @@ ${error51.stderrTail}`] : []
21841
22226
  }
21842
22227
  };
21843
22228
  const selectExternalThinking = async (mounted, thinkingOptionId) => {
22229
+ controller.clearPendingSubmission(mounted.composer);
21844
22230
  const current = controller.get(mounted.composer);
21845
22231
  if (current.agent === "codex") return;
21846
22232
  const agent = current.agent;
@@ -21919,7 +22305,18 @@ ${error51.stderrTail}`] : []
21919
22305
  }
21920
22306
  if (!isCurrentModelRequest(mounted, generation)) return;
21921
22307
  controller.setExternalThinkingOption(mounted.composer, agent, effectiveThinkingOptionId);
21922
- writeNewThreadExternalConfigurationPreference(agent, model, effectiveThinkingOptionId);
22308
+ const effectivePermissionModeId = mounted.threadConfiguration?.effectivePermissionModeId ?? permissionModeId;
22309
+ if (effectivePermissionModeId) {
22310
+ controller.setExternalPermissionMode(mounted.composer, agent, effectivePermissionModeId);
22311
+ }
22312
+ if (shouldPersistNewThreadConfigurationSelection(current.phase)) {
22313
+ writeNewThreadExternalConfigurationPreference(
22314
+ agent,
22315
+ model,
22316
+ effectiveThinkingOptionId,
22317
+ effectivePermissionModeId
22318
+ );
22319
+ }
21923
22320
  mounted.modelView = {
21924
22321
  status: "ready",
21925
22322
  catalog: effectiveCatalog,
@@ -21943,14 +22340,19 @@ ${error51.stderrTail}`] : []
21943
22340
  }
21944
22341
  };
21945
22342
  const switchComposerAgent = async (mounted, agent) => {
21946
- if (agent !== "codex" && harnessAvailability[agent] !== "ready") return false;
22343
+ if (agent !== "codex" && activeHarnessAvailabilityState().availability[agent] !== "ready") {
22344
+ return false;
22345
+ }
22346
+ controller.clearPendingSubmission(mounted.composer);
21947
22347
  const composerId = controller.get(mounted.composer).composerId;
21948
22348
  controller.invalidateModelRequests(mounted.composer);
21949
22349
  const switching = controller.switchAgent(mounted.composer, agent, {
21950
22350
  applyAgent(nextAgent) {
22351
+ const model = controller.modelForAgent(mounted.composer, nextAgent);
22352
+ if (!shouldApplyDraftAgentCarrier(nextAgent, model)) return true;
21951
22353
  return applyAdapterAgent?.(
21952
22354
  nextAgent,
21953
- controller.modelForAgent(mounted.composer, nextAgent),
22355
+ model,
21954
22356
  nextAgent !== "codex" ? controller.thinkingOptionForAgent(mounted.composer, nextAgent) : void 0,
21955
22357
  nextAgent !== "codex" ? controller.permissionModeForAgent(mounted.composer, nextAgent) : void 0,
21956
22358
  mounted.composer
@@ -21987,53 +22389,67 @@ ${error51.stderrTail}`] : []
21987
22389
  const url2 = RENDERER_AGENT_INSTALL_URLS[agent];
21988
22390
  window.open(url2, "_blank", "noopener,noreferrer");
21989
22391
  };
21990
- const resetHarnessAvailabilityRetry = () => {
21991
- if (availabilityRetryTimer !== null) {
21992
- window.clearTimeout(availabilityRetryTimer);
21993
- availabilityRetryTimer = null;
22392
+ function resetHarnessAvailabilityRetry(hostId) {
22393
+ const state = hostHarnessAvailabilityState(hostId);
22394
+ if (state.retryTimer !== null) {
22395
+ window.clearTimeout(state.retryTimer);
22396
+ state.retryTimer = null;
21994
22397
  }
21995
- availabilityRetryAttempt = 0;
21996
- };
21997
- const scheduleHarnessAvailabilityRetry = () => {
21998
- if (disposed || availabilityRetryTimer !== null || availabilityRetryAttempt >= availabilityRetryDelays.length) {
22398
+ state.retryAttempt = 0;
22399
+ }
22400
+ function scheduleHarnessAvailabilityRetry(hostId) {
22401
+ const state = hostHarnessAvailabilityState(hostId);
22402
+ if (disposed || state.retryTimer !== null || state.retryAttempt >= availabilityRetryDelays.length) {
21999
22403
  return;
22000
22404
  }
22001
- const delay = availabilityRetryDelays[availabilityRetryAttempt];
22002
- availabilityRetryAttempt += 1;
22003
- availabilityRetryTimer = window.setTimeout(() => {
22004
- availabilityRetryTimer = null;
22005
- void refreshHarnessAvailability(true, true);
22405
+ const delay = availabilityRetryDelays[state.retryAttempt];
22406
+ state.retryAttempt += 1;
22407
+ state.retryTimer = window.setTimeout(() => {
22408
+ state.retryTimer = null;
22409
+ void refreshHarnessAvailabilityForHost(hostId, true, true);
22006
22410
  }, delay);
22007
- };
22008
- const refreshHarnessAvailability = (refresh = false, retry = false) => {
22009
- if (!retry) resetHarnessAvailabilityRetry();
22010
- if (!modelControl) return Promise.resolve();
22011
- const client = modelControl;
22012
- if (availabilityRequest?.client === client) return availabilityRequest.promise;
22013
- harnessAvailability = Object.fromEntries(
22411
+ }
22412
+ function modelClientForHost(hostId) {
22413
+ if (!modelControl) return null;
22414
+ const selected = modelControl.clientForHost?.(hostId);
22415
+ if (selected) return selected;
22416
+ const currentHostId = modelControl.currentHostId?.() ?? "local";
22417
+ return currentHostId === hostId ? modelControl : null;
22418
+ }
22419
+ function refreshHarnessAvailabilityForHost(hostId, refresh = false, retry = false) {
22420
+ const state = hostHarnessAvailabilityState(hostId);
22421
+ if (!retry) resetHarnessAvailabilityRetry(hostId);
22422
+ const client = modelClientForHost(hostId);
22423
+ if (!client) {
22424
+ scheduleHarnessAvailabilityRetry(hostId);
22425
+ return Promise.resolve();
22426
+ }
22427
+ if (state.request?.client === client) return state.request.promise;
22428
+ state.availability = Object.fromEntries(
22014
22429
  externalAgents.map((agent) => [
22015
22430
  agent,
22016
- harnessAvailability[agent] === "ready" ? "ready" : "checking"
22431
+ state.availability[agent] === "ready" ? "ready" : "checking"
22017
22432
  ])
22018
22433
  );
22019
- publishConnectionStatus();
22020
- for (const mounted of mountedByComposer.values()) renderMounted(mounted);
22021
- const generation = ++availabilityRequestGeneration;
22434
+ if (hostId === activeAvailabilityHostId) {
22435
+ publishConnectionStatus();
22436
+ for (const mounted of mountedByComposer.values()) renderMounted(mounted);
22437
+ }
22438
+ const generation = ++state.requestGeneration;
22022
22439
  const promise2 = (async () => {
22023
22440
  await Promise.all(
22024
22441
  externalAgents.map(async (agent) => {
22025
22442
  let status = "error";
22443
+ let nextError;
22026
22444
  try {
22027
22445
  const inspection = await client.inspectHarness({
22028
22446
  harnessId: externalHarnessIds[agent],
22029
22447
  refresh
22030
22448
  });
22031
22449
  status = inspection.status === "ready" ? "ready" : inspection.status;
22032
- if (inspection.status === "ready") {
22033
- harnessAvailabilityErrors[agent] = void 0;
22034
- } else {
22450
+ if (inspection.status !== "ready") {
22035
22451
  const error51 = inspection.error;
22036
- harnessAvailabilityErrors[agent] = {
22452
+ nextError = {
22037
22453
  code: error51.code,
22038
22454
  message: error51.message,
22039
22455
  retryable: error51.retryable,
@@ -22045,24 +22461,29 @@ ${error51.stderrTail}`] : []
22045
22461
  }
22046
22462
  } catch (error51) {
22047
22463
  status = "error";
22048
- harnessAvailabilityErrors[agent] = {
22464
+ nextError = {
22049
22465
  code: "internalError",
22050
22466
  message: error51 instanceof Error ? error51.message : String(error51),
22051
22467
  retryable: true,
22052
22468
  stage: "request"
22053
22469
  };
22054
22470
  }
22055
- if (generation !== availabilityRequestGeneration || disposed) return;
22056
- harnessAvailability = { ...harnessAvailability, [agent]: status };
22471
+ if (generation !== state.requestGeneration || disposed) return;
22472
+ state.errors[agent] = nextError;
22473
+ state.availability = { ...state.availability, [agent]: status };
22474
+ if (hostId !== activeAvailabilityHostId) {
22475
+ publishConnectionStatus();
22476
+ return;
22477
+ }
22057
22478
  for (const mounted of mountedByComposer.values()) {
22058
- const state = controller.get(mounted.composer);
22059
- if (adapterStatus.state === "ready" && state.phase === "draft" && state.agent === agent && status !== "ready") {
22479
+ const composerState = controller.get(mounted.composer);
22480
+ if (adapterStatus.state === "ready" && composerState.phase === "draft" && composerState.agent === agent && status !== "ready") {
22060
22481
  await switchComposerAgent(mounted, "codex");
22061
22482
  }
22062
22483
  }
22063
22484
  for (const mounted of mountedByComposer.values()) {
22064
- const state = controller.get(mounted.composer);
22065
- if (state.agent === agent) {
22485
+ const composerState = controller.get(mounted.composer);
22486
+ if (composerState.agent === agent) {
22066
22487
  void loadExternalCatalog(mounted);
22067
22488
  }
22068
22489
  renderMounted(mounted);
@@ -22070,37 +22491,66 @@ ${error51.stderrTail}`] : []
22070
22491
  publishConnectionStatus();
22071
22492
  })
22072
22493
  );
22073
- if (externalAgents.every((agent) => harnessAvailability[agent] === "ready")) {
22074
- resetHarnessAvailabilityRetry();
22494
+ if (generation !== state.requestGeneration || disposed) return;
22495
+ if (externalAgents.every((agent) => state.availability[agent] === "ready")) {
22496
+ resetHarnessAvailabilityRetry(hostId);
22075
22497
  } else {
22076
- scheduleHarnessAvailabilityRetry();
22498
+ scheduleHarnessAvailabilityRetry(hostId);
22077
22499
  }
22078
22500
  })();
22079
22501
  const request = { client, promise: promise2 };
22080
- availabilityRequest = request;
22502
+ state.request = request;
22081
22503
  void promise2.then(
22082
22504
  () => {
22083
- if (availabilityRequest === request) availabilityRequest = null;
22505
+ if (state.request === request) state.request = null;
22084
22506
  },
22085
22507
  () => {
22086
- if (availabilityRequest === request) availabilityRequest = null;
22508
+ if (state.request === request) state.request = null;
22087
22509
  }
22088
22510
  );
22089
22511
  return promise2;
22512
+ }
22513
+ function reconcileHarnessAvailabilityHost() {
22514
+ const reportedHostId = modelControl?.currentHostId?.();
22515
+ const hostId = reportedHostId ?? (modelControl?.currentHostId ? activeAvailabilityHostId : "local");
22516
+ if (hostId === activeAvailabilityHostId) return;
22517
+ activeAvailabilityHostId = hostId;
22518
+ hostHarnessAvailabilityState(hostId);
22519
+ publishConnectionStatus();
22520
+ for (const mounted of mountedByComposer.values()) renderMounted(mounted);
22521
+ void refreshHarnessAvailabilityForHost(hostId);
22522
+ }
22523
+ const refreshHarnessAvailability = (refresh = false) => {
22524
+ reconcileHarnessAvailabilityHost();
22525
+ return refreshHarnessAvailabilityForHost(activeAvailabilityHostId, refresh);
22090
22526
  };
22091
22527
  connectionDiagnostics = {
22092
22528
  snapshot() {
22529
+ const hostIds = [
22530
+ "local",
22531
+ ...[...harnessAvailabilityByHost.keys()].filter((hostId) => hostId !== "local").sort()
22532
+ ];
22093
22533
  return {
22094
22534
  adapter: { ...adapterStatus },
22095
- agents: externalAgents.map((agent) => ({
22096
- agent,
22097
- availability: harnessAvailability[agent] ?? "checking",
22098
- error: harnessAvailabilityErrors[agent] ?? null
22099
- }))
22535
+ hosts: hostIds.map((hostId) => {
22536
+ const state = hostHarnessAvailabilityState(hostId);
22537
+ return {
22538
+ hostId,
22539
+ active: hostId === activeAvailabilityHostId,
22540
+ agents: externalAgents.map((agent) => ({
22541
+ agent,
22542
+ availability: state.availability[agent] ?? "checking",
22543
+ error: state.errors[agent] ?? null
22544
+ }))
22545
+ };
22546
+ })
22100
22547
  };
22101
22548
  },
22102
22549
  refresh() {
22103
- return refreshHarnessAvailability(true);
22550
+ for (const hostId of harnessAvailabilityByHost.keys()) {
22551
+ void refreshHarnessAvailabilityForHost(hostId, true);
22552
+ }
22553
+ return Promise.resolve();
22104
22554
  },
22105
22555
  subscribe(listener) {
22106
22556
  connectionListeners.add(listener);
@@ -22169,13 +22619,16 @@ ${error51.stderrTail}`] : []
22169
22619
  };
22170
22620
  mountedByComposer.set(composer, mounted);
22171
22621
  if (isComposerModelWriteAllowed(modelTarget)) {
22172
- applyAdapterAgent?.(
22173
- state.agent,
22174
- controller.modelForAgent(composer, state.agent),
22175
- state.agent !== "codex" ? controller.thinkingOptionForAgent(composer, state.agent) : void 0,
22176
- state.agent !== "codex" ? controller.permissionModeForAgent(composer, state.agent) : void 0,
22177
- composer
22178
- );
22622
+ const model = controller.modelForAgent(composer, state.agent);
22623
+ if (shouldApplyDraftAgentCarrier(state.agent, model)) {
22624
+ applyAdapterAgent?.(
22625
+ state.agent,
22626
+ model,
22627
+ state.agent !== "codex" ? controller.thinkingOptionForAgent(composer, state.agent) : void 0,
22628
+ state.agent !== "codex" ? controller.permissionModeForAgent(composer, state.agent) : void 0,
22629
+ composer
22630
+ );
22631
+ }
22179
22632
  }
22180
22633
  renderMounted(mounted);
22181
22634
  sidebarAgentIcons.refresh();
@@ -22200,7 +22653,8 @@ ${error51.stderrTail}`] : []
22200
22653
  if (!shouldTransferComposerState(
22201
22654
  replacement.sourceModelTarget,
22202
22655
  replacementTarget,
22203
- sourceState.phase
22656
+ sourceState.phase,
22657
+ controller.isSubmissionPending(replacement.source.composer)
22204
22658
  ) || !controller.transfer(replacement.source.composer, target, replacementTarget)) {
22205
22659
  pendingReplacements.delete(target);
22206
22660
  }
@@ -22227,6 +22681,16 @@ ${error51.stderrTail}`] : []
22227
22681
  const composer = composerForEditor(editor);
22228
22682
  if (composer) mount(composer);
22229
22683
  }
22684
+ const localAvailability = hostHarnessAvailabilityState("local").availability;
22685
+ if (externalAgents.some((agent) => localAvailability[agent] === "checking")) {
22686
+ void refreshHarnessAvailabilityForHost("local");
22687
+ }
22688
+ reconcileHarnessAvailabilityHost();
22689
+ if (externalAgents.some(
22690
+ (agent) => activeHarnessAvailabilityState().availability[agent] === "checking"
22691
+ )) {
22692
+ void refreshHarnessAvailability();
22693
+ }
22230
22694
  pendingReplacements.clear();
22231
22695
  };
22232
22696
  const scheduleScan = (refreshTargets = false) => {
@@ -22282,11 +22746,13 @@ ${error51.stderrTail}`] : []
22282
22746
  return state.phase === "locked" && mounted.ownershipStatus === "ready";
22283
22747
  }
22284
22748
  if (!mounted || !isComposerModelWriteAllowed(mounted.modelTarget)) return false;
22749
+ const model = controller.modelForAgent(composer, state.agent);
22750
+ if (!shouldApplyDraftAgentCarrier(state.agent, model)) return false;
22285
22751
  return applyComposerModelWrite(
22286
22752
  mounted.modelTarget,
22287
22753
  () => applyAdapterAgent?.(
22288
22754
  state.agent,
22289
- controller.modelForAgent(composer, state.agent),
22755
+ model,
22290
22756
  state.agent !== "codex" ? controller.thinkingOptionForAgent(composer, state.agent) : void 0,
22291
22757
  state.agent !== "codex" ? controller.permissionModeForAgent(composer, state.agent) : void 0,
22292
22758
  composer
@@ -22308,7 +22774,7 @@ ${error51.stderrTail}`] : []
22308
22774
  if (!isExternalConfigurationReady(mounted)) return false;
22309
22775
  if (current.phase === "locked") return true;
22310
22776
  if (!applyComposerAgent(composer)) return false;
22311
- controller.lock(composer);
22777
+ controller.markSubmissionPending(composer);
22312
22778
  renderMounted(mounted);
22313
22779
  return true;
22314
22780
  };
@@ -22321,6 +22787,7 @@ ${error51.stderrTail}`] : []
22321
22787
  const onBeforeInput = (event) => {
22322
22788
  const composer = composerForTarget(event.target);
22323
22789
  if (!composer) return;
22790
+ controller.clearPendingSubmission(composer);
22324
22791
  const mounted = mountedByComposer.get(composer);
22325
22792
  if (mounted && isOwnershipSubmissionBlocked(mounted.ownershipStatus)) return;
22326
22793
  if (controller.isSwitching(composer) || !applyComposerAgent(composer)) blockEvent(event);
@@ -22378,10 +22845,15 @@ ${error51.stderrTail}`] : []
22378
22845
  transferReplacedComposers(mutations);
22379
22846
  scheduleScan(mutations.some(mutationMayChangeComposerTarget));
22380
22847
  });
22848
+ const onHostRouteChange = () => {
22849
+ reconcileHarnessAvailabilityHost();
22850
+ void refreshHarnessAvailability();
22851
+ };
22381
22852
  const onAdapterStatus = () => {
22382
22853
  publishConnectionStatus();
22383
22854
  if (adapterStatus.state === "ready") {
22384
22855
  sidebarAgentIcons.refresh();
22856
+ void refreshHarnessAvailabilityForHost("local");
22385
22857
  void refreshHarnessAvailability();
22386
22858
  for (const mounted of mountedByComposer.values()) {
22387
22859
  if (mounted.modelView.status === "waitingForAdapter" && mounted.composer.isConnected && applyComposerAgent(mounted.composer)) {
@@ -22403,10 +22875,17 @@ ${error51.stderrTail}`] : []
22403
22875
  document.addEventListener("keydown", onKeyDown, true);
22404
22876
  document.addEventListener("click", onClick, true);
22405
22877
  const onWindowFocus = () => {
22406
- if (externalAgents.some((agent) => harnessAvailability[agent] !== "ready")) {
22878
+ reconcileHarnessAvailabilityHost();
22879
+ const local = hostHarnessAvailabilityState("local");
22880
+ if (externalAgents.some((agent) => local.availability[agent] !== "ready")) {
22881
+ void refreshHarnessAvailabilityForHost("local", true);
22882
+ }
22883
+ const active = activeHarnessAvailabilityState();
22884
+ if (externalAgents.some((agent) => active.availability[agent] !== "ready")) {
22407
22885
  void refreshHarnessAvailability(true);
22408
22886
  }
22409
22887
  };
22888
+ window.addEventListener("codexhost:draft-prewarm-policy-changed", onHostRouteChange);
22410
22889
  window.addEventListener("codexhost:renderer-adapter-status", onAdapterStatus);
22411
22890
  window.addEventListener("focus", onWindowFocus);
22412
22891
  const connectedComposers = () => [...mountedByComposer.values()].filter(
@@ -22423,7 +22902,7 @@ ${error51.stderrTail}`] : []
22423
22902
  version: 2,
22424
22903
  mountedComposers: selections.length,
22425
22904
  enabledAgents: [...enabledAgents],
22426
- availability: { ...harnessAvailability },
22905
+ availability: { ...activeHarnessAvailabilityState().availability },
22427
22906
  selections,
22428
22907
  adapter: { ...adapterStatus }
22429
22908
  };
@@ -22470,8 +22949,16 @@ ${error51.stderrTail}`] : []
22470
22949
  } catch {
22471
22950
  }
22472
22951
  });
22952
+ for (const state of harnessAvailabilityByHost.values()) {
22953
+ state.requestGeneration += 1;
22954
+ state.request = null;
22955
+ if (state.retryTimer !== null) window.clearTimeout(state.retryTimer);
22956
+ }
22957
+ harnessAvailabilityByHost.clear();
22958
+ activeAvailabilityHostId = "local";
22473
22959
  sidebarAgentIcons.refresh();
22474
- void refreshHarnessAvailability();
22960
+ void refreshHarnessAvailabilityForHost("local");
22961
+ reconcileHarnessAvailabilityHost();
22475
22962
  const connected = connectedComposers();
22476
22963
  if (connected.length === 1) {
22477
22964
  const mounted = connected[0];
@@ -22504,9 +22991,14 @@ ${error51.stderrTail}`] : []
22504
22991
  document.removeEventListener("submit", onSubmit, true);
22505
22992
  document.removeEventListener("keydown", onKeyDown, true);
22506
22993
  document.removeEventListener("click", onClick, true);
22994
+ window.removeEventListener("codexhost:draft-prewarm-policy-changed", onHostRouteChange);
22507
22995
  window.removeEventListener("codexhost:renderer-adapter-status", onAdapterStatus);
22508
22996
  window.removeEventListener("focus", onWindowFocus);
22509
- resetHarnessAvailabilityRetry();
22997
+ for (const state of harnessAvailabilityByHost.values()) {
22998
+ state.requestGeneration += 1;
22999
+ if (state.retryTimer !== null) window.clearTimeout(state.retryTimer);
23000
+ }
23001
+ harnessAvailabilityByHost.clear();
22510
23002
  for (const timer of usageRefreshTimers.values()) window.clearTimeout(timer);
22511
23003
  usageRefreshTimers.clear();
22512
23004
  for (const mounted of mountedByComposer.values()) {