@codexhost/cli-darwin-x64 0.2.4 → 0.2.6

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.
@@ -14848,8 +14848,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14848
14848
  code: external_exports.string().min(1),
14849
14849
  message: external_exports.string().min(1),
14850
14850
  retryable: external_exports.boolean(),
14851
- diagnostic: external_exports.string().min(1).optional()
14852
- }).superRefine(rejectExplicitUndefined(["diagnostic"]));
14851
+ diagnostic: external_exports.string().min(1).optional(),
14852
+ stage: external_exports.string().min(1).optional(),
14853
+ durationMs: external_exports.number().int().nonnegative().optional(),
14854
+ stderrTail: external_exports.string().min(1).optional()
14855
+ }).superRefine(rejectExplicitUndefined(["diagnostic", "stage", "durationMs", "stderrTail"]));
14853
14856
 
14854
14857
  // ../shared-contracts/dist/ids.js
14855
14858
  var opaqueIdSchema = external_exports.string().refine((value) => value.trim().length > 0, {
@@ -15194,6 +15197,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15194
15197
  }
15195
15198
  });
15196
15199
 
15200
+ // ../shared-contracts/dist/harness-commands.js
15201
+ var commandIdSchema = external_exports.string().trim().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/u).brand();
15202
+ var commandInvocationSchema = external_exports.string().min(1).max(128);
15203
+ var commandLabelSchema = external_exports.string().trim().min(1).max(128);
15204
+ var commandDescriptionSchema = external_exports.string().trim().min(1).max(512);
15205
+ var harnessCommandDescriptorSchema = external_exports.object({
15206
+ id: commandIdSchema,
15207
+ invocation: commandInvocationSchema,
15208
+ label: commandLabelSchema,
15209
+ description: commandDescriptionSchema.optional(),
15210
+ argumentMode: external_exports.enum(["none", "text"])
15211
+ }).strict();
15212
+ var harnessCommandCatalogSchema = external_exports.object({
15213
+ commands: external_exports.array(harnessCommandDescriptorSchema)
15214
+ }).strict().superRefine((catalog, context) => {
15215
+ const ids = /* @__PURE__ */ new Set();
15216
+ for (const [index, command] of catalog.commands.entries()) {
15217
+ if (ids.has(command.id)) {
15218
+ context.addIssue({
15219
+ code: "custom",
15220
+ message: "Harness command IDs must be unique",
15221
+ path: ["commands", index, "id"]
15222
+ });
15223
+ }
15224
+ ids.add(command.id);
15225
+ }
15226
+ });
15227
+ var threadCommandsInspectParamsSchema = external_exports.object({
15228
+ threadId: hostThreadIdSchema
15229
+ }).strict();
15230
+ var threadCommandExecuteParamsSchema = external_exports.object({
15231
+ threadId: hostThreadIdSchema,
15232
+ commandId: commandIdSchema,
15233
+ turnId: hostTurnIdSchema.optional(),
15234
+ arguments: jsonObjectSchema.optional()
15235
+ }).strict();
15236
+ var threadCommandExecuteResultSchema = external_exports.object({
15237
+ accepted: external_exports.literal(true),
15238
+ turnId: hostTurnIdSchema
15239
+ }).strict();
15240
+
15197
15241
  // ../shared-contracts/dist/json-rpc.js
15198
15242
  var jsonRpcVersionSchema = external_exports.literal("2.0").optional();
15199
15243
  var absentSchema = external_exports.never().optional();
@@ -15405,6 +15449,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15405
15449
  grok: "https://grok.com/"
15406
15450
  };
15407
15451
  var CONTROL_ATTRIBUTE = "data-codexhost-agent-control";
15452
+ var AGENT_MENU_WIDTH = 200;
15408
15453
  function rendererAgentPickerView(state, adapterState, switching, agents, availability = {}) {
15409
15454
  const optionDisabled = Object.fromEntries(
15410
15455
  agents.map((agent) => [
@@ -15425,7 +15470,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15425
15470
  }
15426
15471
  function setMenuPosition(control) {
15427
15472
  const rect = control.trigger.getBoundingClientRect();
15428
- const width = 190;
15473
+ const width = AGENT_MENU_WIDTH;
15429
15474
  const left = Math.max(8, Math.min(rect.right - width, window.innerWidth - width - 8));
15430
15475
  control.menu.style.left = `${left}px`;
15431
15476
  control.menu.style.bottom = `${Math.max(8, window.innerHeight - rect.top + 6)}px`;
@@ -15495,13 +15540,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15495
15540
  menu.hidden = typeof menu.showPopover !== "function";
15496
15541
  menu.style.position = "fixed";
15497
15542
  menu.style.inset = "auto";
15498
- menu.style.width = "190px";
15543
+ menu.style.width = `${AGENT_MENU_WIDTH}px`;
15499
15544
  menu.style.padding = "4px";
15500
15545
  menu.style.border = "0";
15501
15546
  menu.style.borderRadius = "6px";
15502
15547
  menu.style.background = "Canvas";
15503
15548
  menu.style.color = "CanvasText";
15504
15549
  menu.style.boxShadow = "0 8px 24px rgba(0, 0, 0, 0.28)";
15550
+ menu.style.boxSizing = "border-box";
15551
+ menu.style.overflowX = "hidden";
15552
+ menu.style.overflowY = "auto";
15505
15553
  menu.style.zIndex = "2147483647";
15506
15554
  trigger.setAttribute("aria-controls", menu.id);
15507
15555
  const options = {};
@@ -15533,6 +15581,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15533
15581
  button.style.display = "flex";
15534
15582
  button.style.alignItems = "center";
15535
15583
  button.style.gap = "8px";
15584
+ button.style.minWidth = "0";
15536
15585
  button.style.width = "100%";
15537
15586
  button.style.flex = "1 1 auto";
15538
15587
  button.style.height = "36px";
@@ -15556,8 +15605,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15556
15605
  const check2 = document.createElement("span");
15557
15606
  check2.textContent = "\u2713";
15558
15607
  check2.setAttribute("aria-hidden", "true");
15559
- check2.style.width = "12px";
15608
+ check2.style.width = "24px";
15560
15609
  check2.style.flex = "none";
15610
+ check2.style.textAlign = "center";
15561
15611
  check2.style.visibility = "hidden";
15562
15612
  const label = document.createElement("span");
15563
15613
  label.textContent = RENDERER_AGENT_LABELS[agent];
@@ -15566,7 +15616,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15566
15616
  label.style.overflow = "hidden";
15567
15617
  label.style.textOverflow = "ellipsis";
15568
15618
  label.style.whiteSpace = "nowrap";
15569
- button.append(check2, createRendererAgentIcon(agent), label);
15619
+ button.append(createRendererAgentIcon(agent), label);
15570
15620
  button.addEventListener("click", () => {
15571
15621
  const selected = button.getAttribute("aria-pressed") === "true";
15572
15622
  close();
@@ -15576,9 +15626,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15576
15626
  const download = agent === "codex" ? null : (() => {
15577
15627
  const control2 = document.createElement("button");
15578
15628
  control2.type = "button";
15579
- control2.textContent = "\u2193";
15629
+ control2.textContent = "+";
15580
15630
  control2.setAttribute("aria-label", `Install ${RENDERER_AGENT_LABELS[agent]}`);
15581
- control2.title = `Open ${RENDERER_AGENT_LABELS[agent]} installation page`;
15631
+ control2.title = `Install ${RENDERER_AGENT_LABELS[agent]}`;
15632
+ control2.style.position = "absolute";
15633
+ control2.style.inset = "0";
15582
15634
  control2.style.display = "inline-flex";
15583
15635
  control2.style.alignItems = "center";
15584
15636
  control2.style.justifyContent = "center";
@@ -15591,7 +15643,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15591
15643
  control2.style.background = "transparent";
15592
15644
  control2.style.color = "inherit";
15593
15645
  control2.style.cursor = "pointer";
15594
- control2.style.font = "600 16px/1 system-ui, sans-serif";
15646
+ control2.style.font = "600 18px/1 system-ui, sans-serif";
15595
15647
  control2.style.opacity = "0.72";
15596
15648
  control2.addEventListener("pointerenter", () => {
15597
15649
  if (!control2.disabled) control2.style.background = "rgba(127, 127, 127, 0.16)";
@@ -15606,16 +15658,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15606
15658
  return control2;
15607
15659
  })();
15608
15660
  options[agent] = { button, check: check2, download };
15609
- if (download) {
15610
- const row = document.createElement("div");
15611
- row.style.display = "flex";
15612
- row.style.alignItems = "center";
15613
- row.style.gap = "2px";
15614
- row.append(button, download);
15615
- menu.append(row);
15616
- } else {
15617
- menu.append(button);
15618
- }
15661
+ const row = document.createElement("div");
15662
+ row.style.display = "flex";
15663
+ row.style.alignItems = "center";
15664
+ row.style.gap = "2px";
15665
+ const actionSlot = document.createElement("span");
15666
+ actionSlot.style.position = "relative";
15667
+ actionSlot.style.display = "inline-block";
15668
+ actionSlot.style.width = "24px";
15669
+ actionSlot.style.height = "24px";
15670
+ actionSlot.style.flex = "none";
15671
+ actionSlot.append(check2);
15672
+ if (download) actionSlot.append(download);
15673
+ row.append(actionSlot, button);
15674
+ menu.append(row);
15619
15675
  }
15620
15676
  root.append(trigger, menu);
15621
15677
  const onTriggerClick = () => {
@@ -15716,9 +15772,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15716
15772
  option.check.style.visibility = selected ? "visible" : "hidden";
15717
15773
  if (option.download) {
15718
15774
  const visible = view.downloadVisible[agent] === true;
15719
- option.download.hidden = !visible;
15775
+ option.download.hidden = false;
15720
15776
  option.download.disabled = !visible;
15721
- option.download.style.display = visible ? "inline-flex" : "none";
15777
+ option.download.setAttribute("aria-hidden", String(!visible));
15778
+ option.download.style.display = "inline-flex";
15779
+ option.download.style.visibility = visible ? "visible" : "hidden";
15780
+ option.download.style.pointerEvents = visible ? "auto" : "none";
15722
15781
  }
15723
15782
  }
15724
15783
  return view;
@@ -15727,19 +15786,57 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15727
15786
  // src/renderer-model-picker.ts
15728
15787
  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";
15729
15788
  var MENU_CLASSES = "fixed z-50 overflow-hidden rounded-xl bg-token-dropdown-background/90 text-token-foreground shadow-lg backdrop-blur-xl";
15789
+ 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";
15730
15790
  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";
15731
15791
  var HEADING_CLASSES = "px-2 pb-1 pt-1.5 text-sm text-token-text-tertiary";
15732
- var MAIN_MENU_MIN_WIDTH = 180;
15733
- var MAIN_MENU_MAX_WIDTH = 210;
15734
- var MODEL_MENU_PREFERRED_WIDTH = 320;
15735
- var MODEL_MENU_MAX_WIDTH = 380;
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;
15736
15796
  var MODEL_MENU_MAX_HEIGHT = 360;
15797
+ var MODEL_TRIGGER_MAX_WIDTH = "min(200px, 26vw)";
15737
15798
  var MAIN_MENU_LEFT_OFFSET = 96;
15738
15799
  var MENU_GAP = 4;
15739
15800
  var VIEWPORT_MARGIN = 8;
15801
+ var MODEL_SCROLLBAR_STYLE_ATTRIBUTE = "data-codexhost-model-picker-scrollbar";
15740
15802
  function popoverOpen2(menu) {
15741
15803
  return menu.matches(":popover-open");
15742
15804
  }
15805
+ function ensureModelScrollbarStyle(ownerDocument) {
15806
+ if (ownerDocument.querySelector(`style[${MODEL_SCROLLBAR_STYLE_ATTRIBUTE}]`)) return;
15807
+ const style = ownerDocument.createElement("style");
15808
+ style.setAttribute(MODEL_SCROLLBAR_STYLE_ATTRIBUTE, "true");
15809
+ style.textContent = `
15810
+ [data-codexhost-model-scrollable] {
15811
+ scrollbar-width: thin;
15812
+ scrollbar-color: rgba(255, 255, 255, 0.28) transparent;
15813
+ }
15814
+ [data-codexhost-model-scrollable]::-webkit-scrollbar {
15815
+ width: 6px;
15816
+ height: 6px;
15817
+ }
15818
+ [data-codexhost-model-scrollable]::-webkit-scrollbar-track {
15819
+ background: transparent;
15820
+ }
15821
+ [data-codexhost-model-scrollable]::-webkit-scrollbar-thumb {
15822
+ min-height: 28px;
15823
+ border: 1px solid transparent;
15824
+ border-radius: 999px;
15825
+ background: rgba(255, 255, 255, 0.28);
15826
+ background-clip: padding-box;
15827
+ }
15828
+ [data-codexhost-model-scrollable]::-webkit-scrollbar-thumb:hover {
15829
+ background: rgba(255, 255, 255, 0.42);
15830
+ background-clip: padding-box;
15831
+ }
15832
+ [data-codexhost-model-scrollable]::-webkit-scrollbar-button {
15833
+ display: none;
15834
+ width: 0;
15835
+ height: 0;
15836
+ }
15837
+ `;
15838
+ (ownerDocument.head ?? ownerDocument.documentElement).append(style);
15839
+ }
15743
15840
  function thinkingOptionsForModel(catalog, selected) {
15744
15841
  const supported = catalog?.models.find(
15745
15842
  (model) => model.ref.id === selected?.id
@@ -15753,6 +15850,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15753
15850
  function shouldCloseRendererModelPicker(view) {
15754
15851
  return isRendererModelPickerDisabled(view) && view.status !== "selecting";
15755
15852
  }
15853
+ function isTransientPickerState(view) {
15854
+ return view.status === "idle" || view.status === "loading";
15855
+ }
15756
15856
  function rendererModelPickerPresentation(view) {
15757
15857
  const selectedModel = view.catalog?.models.find((model) => model.ref.id === view.selected?.id);
15758
15858
  const thinkingOptions = view.thinkingSelectionSupported === false ? [] : thinkingOptionsForModel(view.catalog, view.selected);
@@ -15822,7 +15922,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15822
15922
  function syncRendererModelTriggerClass(control, nativeClassName) {
15823
15923
  control.trigger.className = nativeClassName?.trim() || RENDERER_MODEL_TRIGGER_FALLBACK_CLASSES;
15824
15924
  control.trigger.style.width = "fit-content";
15825
- control.trigger.style.maxWidth = "min(320px, 38vw)";
15925
+ control.trigger.style.maxWidth = MODEL_TRIGGER_MAX_WIDTH;
15826
15926
  }
15827
15927
  function createCheck() {
15828
15928
  const check2 = document.createElement("span");
@@ -15845,6 +15945,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15845
15945
  element.textContent = text;
15846
15946
  return true;
15847
15947
  }
15948
+ function applyModelSearchFilter(control) {
15949
+ const query = control.searchInput.value.trim().toLowerCase();
15950
+ let visibleCount = 0;
15951
+ for (const option of control.options.values()) {
15952
+ const matches = query.length === 0 || option.searchText.includes(query);
15953
+ option.button.hidden = !matches;
15954
+ if (matches) visibleCount += 1;
15955
+ }
15956
+ control.searchEmpty.hidden = query.length === 0 || visibleCount > 0;
15957
+ }
15848
15958
  function mountRendererModelPicker(composerId, nativeClassName, onSelectModel, onSelectThinking) {
15849
15959
  const root = document.createElement("div");
15850
15960
  root.setAttribute("data-codexhost-model-control", composerId);
@@ -15874,7 +15984,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15874
15984
  menu.id = `${composerId}-model-menu`;
15875
15985
  menu.setAttribute("role", "menu");
15876
15986
  menu.setAttribute("aria-label", "Model and Thinking");
15877
- menu.setAttribute("popover", "auto");
15987
+ menu.setAttribute("popover", "manual");
15878
15988
  menu.className = MENU_CLASSES;
15879
15989
  menu.style.position = "fixed";
15880
15990
  menu.style.inset = "auto";
@@ -15895,6 +16005,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15895
16005
  modelMenu.setAttribute("aria-label", "Model");
15896
16006
  modelMenu.setAttribute("popover", "manual");
15897
16007
  modelMenu.className = MENU_CLASSES;
16008
+ modelMenu.dataset.codexhostModelScrollable = "true";
16009
+ ensureModelScrollbarStyle(document);
15898
16010
  modelMenu.style.position = "fixed";
15899
16011
  modelMenu.style.inset = "auto";
15900
16012
  modelMenu.style.margin = "0";
@@ -15905,9 +16017,61 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15905
16017
  modelButton.setAttribute("aria-controls", modelMenu.id);
15906
16018
  const options = /* @__PURE__ */ new Map();
15907
16019
  const thinkingOptions = /* @__PURE__ */ new Map();
16020
+ const searchInput = document.createElement("input");
16021
+ searchInput.type = "search";
16022
+ searchInput.placeholder = "Search models";
16023
+ searchInput.setAttribute("aria-label", "Search models");
16024
+ searchInput.autocomplete = "off";
16025
+ searchInput.spellcheck = false;
16026
+ searchInput.className = SEARCH_INPUT_CLASSES;
16027
+ const searchHeader = document.createElement("div");
16028
+ searchHeader.style.position = "sticky";
16029
+ searchHeader.style.top = "0";
16030
+ searchHeader.style.zIndex = "2";
16031
+ searchHeader.style.margin = "-4px";
16032
+ searchHeader.style.padding = "4px";
16033
+ searchHeader.style.backgroundColor = "Canvas";
16034
+ const searchEmpty = document.createElement("div");
16035
+ searchEmpty.dataset.codexhostModelSearchEmpty = "true";
16036
+ searchEmpty.textContent = "No matching models";
16037
+ searchEmpty.className = "block px-2 py-2 text-sm text-token-text-tertiary";
16038
+ searchEmpty.hidden = true;
16039
+ const onSearchInput = () => applyModelSearchFilter(control);
16040
+ searchInput.addEventListener("input", onSearchInput);
16041
+ const silencedEventTypes = [
16042
+ "keydown",
16043
+ "keypress",
16044
+ "keyup",
16045
+ "beforeinput",
16046
+ "input",
16047
+ "compositionstart",
16048
+ "compositionupdate",
16049
+ "compositionend",
16050
+ "change"
16051
+ ];
16052
+ const silenceForHarness = (event) => {
16053
+ event.stopPropagation();
16054
+ };
16055
+ for (const type of silencedEventTypes) {
16056
+ searchInput.addEventListener(type, silenceForHarness);
16057
+ }
16058
+ const onSearchBlur = () => {
16059
+ if (!popoverOpen2(modelMenu)) return;
16060
+ const active = document.activeElement;
16061
+ const movedToComposer = active === document.body || active instanceof Element && (active.matches('textarea, [contenteditable="true"], [role="textbox"]') || active.closest('textarea, [contenteditable="true"], [role="textbox"]') !== null);
16062
+ if (!movedToComposer) return;
16063
+ requestAnimationFrame(() => {
16064
+ if (popoverOpen2(modelMenu) && searchInput.isConnected) searchInput.focus();
16065
+ });
16066
+ };
16067
+ searchInput.addEventListener("blur", onSearchBlur);
15908
16068
  const closeModelMenu = () => {
15909
16069
  if (popoverOpen2(modelMenu)) modelMenu.hidePopover();
15910
16070
  modelButton.setAttribute("aria-expanded", "false");
16071
+ if (searchInput.value !== "") {
16072
+ searchInput.value = "";
16073
+ applyModelSearchFilter(control);
16074
+ }
15911
16075
  };
15912
16076
  const close = () => {
15913
16077
  closeModelMenu();
@@ -15941,6 +16105,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15941
16105
  const target = event.target instanceof Element ? event.target.closest("button") : null;
15942
16106
  if (target?.dataset.openModelMenu) {
15943
16107
  openModelMenu();
16108
+ control.searchInput.focus();
15944
16109
  return;
15945
16110
  }
15946
16111
  if (target?.dataset.thinkingOptionId) {
@@ -15952,11 +16117,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15952
16117
  const onModelMenuClick = (event) => {
15953
16118
  const target = event.target instanceof Element ? event.target.closest("button[data-model-id]") : null;
15954
16119
  if (!target?.dataset.modelId) return;
15955
- closeModelMenu();
15956
- modelButton.focus();
16120
+ close();
16121
+ trigger.focus();
15957
16122
  onSelectModel(target.dataset.modelId);
15958
16123
  };
15959
16124
  const onModelHover = () => openModelMenu();
16125
+ const onDocumentPointerDown = (event) => {
16126
+ if (!popoverOpen2(menu) && !popoverOpen2(modelMenu)) return;
16127
+ const target = event.target instanceof Node ? event.target : null;
16128
+ if (target && (root.contains(target) || menu.contains(target) || modelMenu.contains(target))) {
16129
+ return;
16130
+ }
16131
+ close();
16132
+ };
16133
+ const onDocumentKeyDown = (event) => {
16134
+ if (event.key !== "Escape") return;
16135
+ if (!popoverOpen2(menu) && !popoverOpen2(modelMenu)) return;
16136
+ event.preventDefault();
16137
+ close();
16138
+ trigger.focus();
16139
+ };
15960
16140
  const onViewportChange = () => {
15961
16141
  if (popoverOpen2(menu)) positionMainMenu(control);
15962
16142
  if (popoverOpen2(modelMenu)) positionModelMenu(control);
@@ -15967,10 +16147,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15967
16147
  modelButton.addEventListener("mouseenter", onModelHover);
15968
16148
  root.addEventListener("click", onRootClick);
15969
16149
  modelMenu.addEventListener("click", onModelMenuClick);
16150
+ document.addEventListener("pointerdown", onDocumentPointerDown, true);
16151
+ document.addEventListener("keydown", onDocumentKeyDown, true);
15970
16152
  window.addEventListener("resize", onViewportChange);
15971
16153
  window.addEventListener("scroll", onViewportChange, true);
15972
16154
  root.append(trigger, menu);
15973
16155
  document.body.append(modelMenu);
16156
+ searchHeader.append(searchInput);
16157
+ modelMenu.append(searchHeader, searchEmpty);
15974
16158
  const control = {
15975
16159
  root,
15976
16160
  trigger,
@@ -15979,6 +16163,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15979
16163
  menu,
15980
16164
  modelMenu,
15981
16165
  modelButton,
16166
+ searchInput,
16167
+ searchHeader,
16168
+ searchEmpty,
15982
16169
  options,
15983
16170
  thinkingOptions,
15984
16171
  close,
@@ -15990,6 +16177,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15990
16177
  modelButton.removeEventListener("mouseenter", onModelHover);
15991
16178
  root.removeEventListener("click", onRootClick);
15992
16179
  modelMenu.removeEventListener("click", onModelMenuClick);
16180
+ searchInput.removeEventListener("input", onSearchInput);
16181
+ for (const type of silencedEventTypes) {
16182
+ searchInput.removeEventListener(type, silenceForHarness);
16183
+ }
16184
+ searchInput.removeEventListener("blur", onSearchBlur);
16185
+ document.removeEventListener("pointerdown", onDocumentPointerDown, true);
16186
+ document.removeEventListener("keydown", onDocumentKeyDown, true);
15993
16187
  window.removeEventListener("resize", onViewportChange);
15994
16188
  window.removeEventListener("scroll", onViewportChange, true);
15995
16189
  modelMenu.remove();
@@ -16004,7 +16198,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16004
16198
  control.options.clear();
16005
16199
  control.thinkingOptions.clear();
16006
16200
  control.menu.replaceChildren();
16007
- control.modelMenu.replaceChildren(createHeading("Model"));
16201
+ control.modelMenu.replaceChildren(
16202
+ createHeading("Model"),
16203
+ control.searchHeader,
16204
+ control.searchEmpty
16205
+ );
16008
16206
  if (presentation.showThinkingSection) {
16009
16207
  control.menu.append(createHeading("Thinking"));
16010
16208
  for (const option of presentation.thinkingOptions) {
@@ -16048,9 +16246,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16048
16246
  text.title = model.label;
16049
16247
  const check2 = createCheck();
16050
16248
  button.append(text, check2);
16051
- control.options.set(model.ref.id, { button, check: check2 });
16249
+ control.options.set(model.ref.id, {
16250
+ button,
16251
+ check: check2,
16252
+ searchText: `${model.label} ${model.ref.id}`.toLowerCase()
16253
+ });
16052
16254
  control.modelMenu.append(button);
16053
16255
  }
16256
+ applyModelSearchFilter(control);
16257
+ if (popoverOpen2(control.modelMenu)) control.searchInput.focus();
16054
16258
  }
16055
16259
  function renderRendererModelPicker(control, view, visible) {
16056
16260
  control.root.style.display = visible ? "block" : "none";
@@ -16065,11 +16269,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16065
16269
  showThinkingSection: presentation.showThinkingSection,
16066
16270
  modelLabel: presentation.modelLabel
16067
16271
  });
16068
- if (control.root.dataset.catalogSignature !== catalogSignature) {
16272
+ const keepOpenMenu = popoverOpen2(control.menu) && isTransientPickerState(view);
16273
+ if (control.root.dataset.catalogSignature !== catalogSignature && !keepOpenMenu) {
16069
16274
  rebuildOptions(control, view);
16070
16275
  control.root.dataset.catalogSignature = catalogSignature;
16071
16276
  }
16072
16277
  syncRendererLabelText(control.label, presentation.modelLabel);
16278
+ control.label.title = presentation.modelLabel;
16073
16279
  const secondaryLabel = presentation.thinkingLabel ?? presentation.resolvedModelLabel;
16074
16280
  syncRendererLabelText(control.thinkingLabel, secondaryLabel ?? "");
16075
16281
  control.thinkingLabel.hidden = secondaryLabel === void 0;
@@ -16081,7 +16287,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16081
16287
  String(view.status === "loading" || view.status === "selecting")
16082
16288
  );
16083
16289
  control.trigger.disabled = isRendererModelPickerDisabled(view);
16084
- if (shouldCloseRendererModelPicker(view)) control.close();
16290
+ if (shouldCloseRendererModelPicker(view) && !keepOpenMenu) control.close();
16085
16291
  control.modelButton.disabled = control.trigger.disabled;
16086
16292
  for (const [modelId, option] of control.options) {
16087
16293
  const selected = modelId === view.selected?.id;
@@ -16928,6 +17134,322 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16928
17134
  return true;
16929
17135
  }
16930
17136
 
17137
+ // src/renderer-harness-command-control.ts
17138
+ var CONTROL_ATTRIBUTE2 = "data-codexhost-harness-command-control";
17139
+ var MENU_ATTRIBUTE = "data-codexhost-harness-command-menu";
17140
+ var MENU_WIDTH = 320;
17141
+ var VIEWPORT_MARGIN2 = 8;
17142
+ var MENU_GAP2 = 8;
17143
+ var SVG_NS = "http://www.w3.org/2000/svg";
17144
+ var COMMAND_ICON_PATHS = [
17145
+ "M409.6 377.6h204.8a32 32 0 0 1 32 32v204.8a32 32 0 0 1-32 32H409.6a32 32 0 0 1-32-32V409.6a32 32 0 0 1 32-32z m172.8 64h-140.8v140.8h140.8z",
17146
+ "M332.8 800a108.8 108.8 0 0 1 0-217.6h76.8a32 32 0 0 1 32 32v76.8a108.928 108.928 0 0 1-108.8 108.8z m0-153.6a44.8 44.8 0 1 0 44.8 44.8v-44.8zM409.6 441.6H332.8a108.8 108.8 0 1 1 108.8-108.8v76.8a32 32 0 0 1-32 32z m-76.8-153.6a44.8 44.8 0 0 0 0 89.6h44.8V332.8A44.842667 44.842667 0 0 0 332.8 288zM691.2 441.6h-76.8a32 32 0 0 1-32-32V332.8a108.8 108.8 0 1 1 108.8 108.8z m-44.8-64h44.8a44.8 44.8 0 1 0-44.8-44.8zM691.2 800a108.928 108.928 0 0 1-108.8-108.8v-76.8a32 32 0 0 1 32-32h76.8a108.8 108.8 0 0 1 0 217.6z m-44.8-153.6v44.8a44.8 44.8 0 1 0 44.8-44.8z",
17147
+ "M640 970.666667H384c-118.186667 0-198.272-25.002667-251.946667-78.72S53.333333 758.186667 53.333333 640V384c0-118.186667 25.002667-198.272 78.72-251.946667S265.813333 53.333333 384 53.333333h256c118.186667 0 198.272 25.002667 251.946667 78.72S970.666667 265.813333 970.666667 384v256c0 118.186667-25.002667 198.272-78.72 251.946667S758.186667 970.666667 640 970.666667z m-256-853.333334c-100.096 0-165.802667 19.2-206.72 59.946667S117.333333 283.904 117.333333 384v256c0 100.096 19.072 165.802667 59.946667 206.72S283.904 906.666667 384 906.666667h256c100.096 0 165.802667-19.072 206.72-59.946667S906.666667 740.096 906.666667 640V384c0-100.096-19.072-165.802667-59.946667-206.72S740.096 117.333333 640 117.333333z"
17148
+ ];
17149
+ function commandIcon(ownerDocument) {
17150
+ const svg = ownerDocument.createElementNS(SVG_NS, "svg");
17151
+ svg.setAttribute("viewBox", "0 0 1024 1024");
17152
+ svg.setAttribute("width", "15");
17153
+ svg.setAttribute("height", "15");
17154
+ svg.setAttribute("fill", "currentColor");
17155
+ svg.setAttribute("aria-hidden", "true");
17156
+ for (const path of COMMAND_ICON_PATHS) {
17157
+ const element = ownerDocument.createElementNS(SVG_NS, "path");
17158
+ element.setAttribute("d", path);
17159
+ svg.append(element);
17160
+ }
17161
+ return svg;
17162
+ }
17163
+ function menuItem(ownerDocument, command, onSelect) {
17164
+ const item = ownerDocument.createElement("button");
17165
+ item.type = "button";
17166
+ item.setAttribute("role", "menuitem");
17167
+ item.setAttribute("data-command-id", command.id);
17168
+ item.setAttribute("aria-label", `${command.invocation} ${command.label}`);
17169
+ item.style.display = "flex";
17170
+ item.style.alignItems = "center";
17171
+ item.style.width = "100%";
17172
+ item.style.minHeight = "38px";
17173
+ item.style.gap = "8px";
17174
+ item.style.padding = "6px 8px";
17175
+ item.style.border = "0";
17176
+ item.style.borderRadius = "6px";
17177
+ item.style.background = "transparent";
17178
+ item.style.color = "inherit";
17179
+ item.style.textAlign = "left";
17180
+ item.style.cursor = "pointer";
17181
+ const updateHighlight = (active) => {
17182
+ item.style.background = active ? "rgba(127, 127, 127, 0.12)" : "transparent";
17183
+ };
17184
+ item.addEventListener("pointerenter", () => updateHighlight(true));
17185
+ item.addEventListener("pointerleave", () => updateHighlight(false));
17186
+ item.addEventListener("focus", () => updateHighlight(true));
17187
+ item.addEventListener("blur", () => updateHighlight(false));
17188
+ item.addEventListener("click", onSelect);
17189
+ const copy = ownerDocument.createElement("span");
17190
+ copy.style.display = "flex";
17191
+ copy.style.flexDirection = "column";
17192
+ copy.style.minWidth = "0";
17193
+ copy.style.flex = "1 1 auto";
17194
+ const title = ownerDocument.createElement("span");
17195
+ title.textContent = command.invocation;
17196
+ title.style.font = "600 13px/18px system-ui, sans-serif";
17197
+ title.style.whiteSpace = "nowrap";
17198
+ const description = ownerDocument.createElement("span");
17199
+ description.textContent = command.description ?? command.label;
17200
+ description.style.overflow = "hidden";
17201
+ description.style.color = "rgba(127, 127, 127, 0.9)";
17202
+ description.style.font = "400 11px/16px system-ui, sans-serif";
17203
+ description.style.textOverflow = "ellipsis";
17204
+ description.style.whiteSpace = "nowrap";
17205
+ const hint = ownerDocument.createElement("span");
17206
+ hint.textContent = command.argumentMode === "text" ? "Text" : "\u21B5";
17207
+ hint.style.flex = "0 0 auto";
17208
+ hint.style.color = "rgba(127, 127, 127, 0.75)";
17209
+ hint.style.font = "400 11px/16px ui-monospace, SFMono-Regular, Menlo, monospace";
17210
+ copy.append(title, description);
17211
+ item.append(copy, hint);
17212
+ return item;
17213
+ }
17214
+ function clamp(value, minimum, maximum) {
17215
+ return Math.max(minimum, Math.min(value, maximum));
17216
+ }
17217
+ function setButtonClass(button) {
17218
+ button.style.display = "inline-flex";
17219
+ button.style.alignItems = "center";
17220
+ button.style.justifyContent = "center";
17221
+ button.style.gap = "0";
17222
+ button.style.width = "28px";
17223
+ button.style.height = "28px";
17224
+ button.style.padding = "0";
17225
+ button.style.border = "0";
17226
+ button.style.borderRadius = "8px";
17227
+ button.style.background = "transparent";
17228
+ button.style.color = "inherit";
17229
+ button.style.cursor = "pointer";
17230
+ button.style.whiteSpace = "nowrap";
17231
+ }
17232
+ function mountRendererHarnessCommandControl(parent, insertBefore, onCommandSelected) {
17233
+ const ownerDocument = parent.ownerDocument;
17234
+ const root = ownerDocument.createElement("div");
17235
+ root.setAttribute(CONTROL_ATTRIBUTE2, "true");
17236
+ root.style.display = "inline-flex";
17237
+ root.style.alignItems = "center";
17238
+ root.style.minWidth = "0";
17239
+ const trigger = ownerDocument.createElement("button");
17240
+ trigger.type = "button";
17241
+ trigger.setAttribute("aria-haspopup", "menu");
17242
+ trigger.setAttribute("aria-expanded", "false");
17243
+ trigger.setAttribute("aria-label", "Harness commands");
17244
+ trigger.title = "Harness commands";
17245
+ setButtonClass(trigger);
17246
+ trigger.append(commandIcon(ownerDocument));
17247
+ root.append(trigger);
17248
+ const menu = ownerDocument.createElement("div");
17249
+ menu.setAttribute(MENU_ATTRIBUTE, "true");
17250
+ menu.setAttribute("role", "menu");
17251
+ menu.setAttribute("aria-label", "Harness commands");
17252
+ menu.hidden = true;
17253
+ menu.style.position = "fixed";
17254
+ menu.style.inset = "auto";
17255
+ menu.style.zIndex = "2147483647";
17256
+ menu.style.width = `${MENU_WIDTH}px`;
17257
+ menu.style.maxWidth = `calc(100vw - ${VIEWPORT_MARGIN2 * 2}px)`;
17258
+ menu.style.maxHeight = "min(360px, calc(100vh - 16px))";
17259
+ menu.style.overflowY = "auto";
17260
+ menu.style.padding = "4px";
17261
+ menu.style.border = "1px solid rgba(127, 127, 127, 0.24)";
17262
+ menu.style.borderRadius = "10px";
17263
+ menu.style.background = "Canvas";
17264
+ menu.style.color = "CanvasText";
17265
+ menu.style.boxShadow = "0 12px 32px rgba(0, 0, 0, 0.22)";
17266
+ ownerDocument.body.append(menu);
17267
+ if (insertBefore?.parentElement === parent) parent.insertBefore(root, insertBefore);
17268
+ else parent.append(root);
17269
+ let commands = [];
17270
+ let items = [];
17271
+ let activeIndex = 0;
17272
+ let executingCommandId = null;
17273
+ let triggerHovered = false;
17274
+ let disposed = false;
17275
+ const positionMenu2 = () => {
17276
+ const rect = trigger.getBoundingClientRect();
17277
+ const menuHeight = menu.getBoundingClientRect().height;
17278
+ const opensAbove = rect.top >= menuHeight + MENU_GAP2 + VIEWPORT_MARGIN2;
17279
+ const left = clamp(
17280
+ rect.left,
17281
+ VIEWPORT_MARGIN2,
17282
+ window.innerWidth - MENU_WIDTH - VIEWPORT_MARGIN2
17283
+ );
17284
+ 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`;
17286
+ };
17287
+ const focusActive = () => {
17288
+ const item = items[activeIndex];
17289
+ if (!item || item.disabled) return;
17290
+ item.focus();
17291
+ item.scrollIntoView({ block: "nearest" });
17292
+ };
17293
+ const syncTriggerBackground = () => {
17294
+ trigger.style.background = !trigger.disabled && (triggerHovered || !menu.hidden) ? "rgba(127, 127, 127, 0.16)" : "transparent";
17295
+ };
17296
+ const close = () => {
17297
+ menu.hidden = true;
17298
+ trigger.setAttribute("aria-expanded", "false");
17299
+ syncTriggerBackground();
17300
+ };
17301
+ const open = (shouldFocus = true) => {
17302
+ if (commands.length === 0 || executingCommandId !== null) return;
17303
+ menu.hidden = false;
17304
+ positionMenu2();
17305
+ trigger.setAttribute("aria-expanded", "true");
17306
+ syncTriggerBackground();
17307
+ if (shouldFocus) queueMicrotask(focusActive);
17308
+ };
17309
+ let closeTimer = null;
17310
+ const cancelClose = () => {
17311
+ if (closeTimer === null) return;
17312
+ window.clearTimeout(closeTimer);
17313
+ closeTimer = null;
17314
+ };
17315
+ const scheduleClose = () => {
17316
+ cancelClose();
17317
+ closeTimer = window.setTimeout(() => {
17318
+ closeTimer = null;
17319
+ if (!trigger.matches(":hover") && !menu.matches(":hover")) close();
17320
+ }, 140);
17321
+ };
17322
+ const select = (command) => {
17323
+ close();
17324
+ onCommandSelected(command);
17325
+ };
17326
+ const renderItems = () => {
17327
+ menu.replaceChildren();
17328
+ const header = ownerDocument.createElement("div");
17329
+ header.textContent = "Commands";
17330
+ header.style.padding = "5px 8px 4px";
17331
+ header.style.color = "rgba(127, 127, 127, 0.75)";
17332
+ header.style.font = "600 11px/16px system-ui, sans-serif";
17333
+ menu.append(header);
17334
+ items = commands.map((command) => menuItem(ownerDocument, command, () => select(command)));
17335
+ menu.append(...items);
17336
+ activeIndex = Math.min(activeIndex, Math.max(0, items.length - 1));
17337
+ if (executingCommandId !== null) {
17338
+ for (const item of items) {
17339
+ const isExecuting = item.dataset.commandId === executingCommandId;
17340
+ item.disabled = true;
17341
+ item.style.opacity = isExecuting ? "1" : "0.5";
17342
+ if (isExecuting) item.setAttribute("aria-busy", "true");
17343
+ }
17344
+ }
17345
+ };
17346
+ const onTriggerKeyDown = (event) => {
17347
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
17348
+ event.preventDefault();
17349
+ open();
17350
+ }
17351
+ };
17352
+ const onMenuKeyDown = (event) => {
17353
+ if (event.key === "Escape") {
17354
+ event.preventDefault();
17355
+ close();
17356
+ trigger.focus();
17357
+ return;
17358
+ }
17359
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
17360
+ event.preventDefault();
17361
+ const delta = event.key === "ArrowDown" ? 1 : -1;
17362
+ activeIndex = (activeIndex + delta + items.length) % items.length;
17363
+ focusActive();
17364
+ return;
17365
+ }
17366
+ if (event.key === "Enter") {
17367
+ event.preventDefault();
17368
+ items[activeIndex]?.click();
17369
+ }
17370
+ };
17371
+ const onDocumentPointerDown = (event) => {
17372
+ if (!menu.hidden && !root.contains(event.target) && !menu.contains(event.target)) {
17373
+ close();
17374
+ }
17375
+ };
17376
+ const onViewportChange = () => {
17377
+ if (!menu.hidden) positionMenu2();
17378
+ };
17379
+ trigger.addEventListener("click", () => {
17380
+ cancelClose();
17381
+ open(true);
17382
+ });
17383
+ trigger.addEventListener("pointerenter", () => {
17384
+ triggerHovered = true;
17385
+ syncTriggerBackground();
17386
+ cancelClose();
17387
+ if (menu.hidden) open(false);
17388
+ });
17389
+ trigger.addEventListener("pointerleave", () => {
17390
+ triggerHovered = false;
17391
+ syncTriggerBackground();
17392
+ scheduleClose();
17393
+ });
17394
+ trigger.addEventListener("keydown", onTriggerKeyDown);
17395
+ menu.addEventListener("pointerenter", cancelClose);
17396
+ menu.addEventListener("pointerleave", scheduleClose);
17397
+ menu.addEventListener("keydown", onMenuKeyDown);
17398
+ ownerDocument.addEventListener("pointerdown", onDocumentPointerDown, true);
17399
+ ownerDocument.defaultView?.addEventListener("resize", onViewportChange);
17400
+ ownerDocument.defaultView?.addEventListener("scroll", onViewportChange, true);
17401
+ const control = {
17402
+ root,
17403
+ trigger,
17404
+ menu,
17405
+ placeBefore(reference) {
17406
+ if (!reference?.parentElement) return false;
17407
+ if (root.parentElement === reference.parentElement && root.nextElementSibling === reference) {
17408
+ return true;
17409
+ }
17410
+ reference.parentElement.insertBefore(root, reference);
17411
+ return true;
17412
+ },
17413
+ setCommands(nextCommands) {
17414
+ commands = [...nextCommands];
17415
+ root.hidden = commands.length === 0;
17416
+ if (commands.length === 0) close();
17417
+ renderItems();
17418
+ },
17419
+ setExecuting(commandId) {
17420
+ executingCommandId = commandId;
17421
+ for (const item of items) {
17422
+ const isExecuting = item.dataset.commandId === commandId;
17423
+ item.disabled = commandId !== null;
17424
+ item.style.opacity = commandId !== null && !isExecuting ? "0.5" : "1";
17425
+ if (isExecuting) item.setAttribute("aria-busy", "true");
17426
+ else item.removeAttribute("aria-busy");
17427
+ }
17428
+ trigger.disabled = commandId !== null;
17429
+ trigger.style.opacity = commandId !== null ? "0.65" : "1";
17430
+ syncTriggerBackground();
17431
+ },
17432
+ close,
17433
+ dispose() {
17434
+ if (disposed) return;
17435
+ disposed = true;
17436
+ cancelClose();
17437
+ close();
17438
+ ownerDocument.removeEventListener("pointerdown", onDocumentPointerDown, true);
17439
+ ownerDocument.defaultView?.removeEventListener("resize", onViewportChange);
17440
+ ownerDocument.defaultView?.removeEventListener("scroll", onViewportChange, true);
17441
+ trigger.removeEventListener("keydown", onTriggerKeyDown);
17442
+ menu.removeEventListener("pointerenter", cancelClose);
17443
+ menu.removeEventListener("pointerleave", scheduleClose);
17444
+ menu.removeEventListener("keydown", onMenuKeyDown);
17445
+ menu.remove();
17446
+ root.remove();
17447
+ }
17448
+ };
17449
+ root.hidden = true;
17450
+ return control;
17451
+ }
17452
+
16931
17453
  // src/renderer-composer-dom.ts
16932
17454
  var CODEX_COMPOSER_SELECTOR = "[data-codex-composer-root]";
16933
17455
  var EDITOR_SELECTOR = 'textarea, [contenteditable="true"], [role="textbox"]';
@@ -17097,7 +17619,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17097
17619
  for (const child of parent.children) {
17098
17620
  if (typeof child.hasAttribute !== "function") continue;
17099
17621
  const element = child;
17100
- if (element.hasAttribute("data-codexhost-credits-control")) continue;
17622
+ if (element.hasAttribute("data-codexhost-credits-control") || element.hasAttribute("data-codexhost-harness-command-control")) {
17623
+ continue;
17624
+ }
17101
17625
  return element;
17102
17626
  }
17103
17627
  return null;
@@ -17123,7 +17647,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17123
17647
  control.credits.anchor = null;
17124
17648
  return;
17125
17649
  }
17650
+ const previousUsageParent = control.usage.root.parentElement;
17651
+ const previousUsageNextSibling = control.usage.root.nextElementSibling;
17126
17652
  control.usage.place(anchor);
17653
+ const usagePositionChanged = previousUsageParent !== control.usage.root.parentElement || previousUsageNextSibling !== control.usage.root.nextElementSibling;
17654
+ if (usagePositionChanged) control.harnessCommands?.placeBefore(control.usage.root);
17127
17655
  const leading = creditsPlacementAnchor(control.composer, control.usage.root);
17128
17656
  if (!leading) {
17129
17657
  if (control.credits.anchor) control.credits.root.remove();
@@ -17167,7 +17695,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17167
17695
  setNativeControlHidden(control.nativeModelControl, hideModel);
17168
17696
  setNativeControlHidden(control.nativePermissionModeControl, hidePermissionMode);
17169
17697
  }
17170
- function mountComposerAgentControl(composer, composerId, sendButton, enabledAgents, onSelect, onDownload, onSelectModel, onSelectThinking, onSelectPermissionMode) {
17698
+ function mountComposerAgentControl(composer, composerId, sendButton, enabledAgents, onSelect, onDownload, onSelectModel, onSelectThinking, onSelectPermissionMode, onSelectCommand) {
17171
17699
  const nativeModelControl = captureNativeControl(nativeModelControlForComposer(composer));
17172
17700
  const semanticNativePermissionModeControl = semanticNativePermissionModeControlForComposer(composer);
17173
17701
  const nativePermissionModeControl = captureNativeControl(semanticNativePermissionModeControl);
@@ -17186,13 +17714,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17186
17714
  );
17187
17715
  const usage = mountRendererUsageControl(composerId, nativeModelControl?.element.className);
17188
17716
  const credits = mountRendererCreditsControl(composerId, nativeModelControl?.element.className);
17717
+ const toolbar = sendButton.parentElement;
17718
+ const harnessCommands = mountRendererHarnessCommandControl(
17719
+ toolbar ?? composer,
17720
+ sendButton,
17721
+ onSelectCommand
17722
+ );
17189
17723
  const permissionParent = nativePermissionModeControl?.element.parentElement;
17190
17724
  if (permissionParent && nativePermissionModeControl && nativePermissionModeControlVerified) {
17191
17725
  permissionParent.insertBefore(permissionModePicker.root, nativePermissionModeControl.element);
17192
17726
  } else {
17193
17727
  composer.append(permissionModePicker.root);
17194
17728
  }
17195
- const toolbar = sendButton.parentElement;
17196
17729
  if (toolbar) {
17197
17730
  toolbar.insertBefore(modelPicker.root, sendButton);
17198
17731
  toolbar.insertBefore(picker.root, sendButton);
@@ -17210,6 +17743,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17210
17743
  nativePermissionModeControlVerified,
17211
17744
  credits,
17212
17745
  usage,
17746
+ harnessCommands,
17213
17747
  sendButton,
17214
17748
  sendDisabledBeforeSwitch: null
17215
17749
  };
@@ -17264,6 +17798,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17264
17798
  restoreNativeControl(control.nativePermissionModeControl);
17265
17799
  control.credits.dispose();
17266
17800
  control.usage.dispose();
17801
+ control.harnessCommands.dispose();
17267
17802
  control.permissionModePicker.dispose();
17268
17803
  control.modelPicker.dispose();
17269
17804
  control.picker.dispose();
@@ -17701,6 +18236,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17701
18236
  var HARNESS_INSPECT_METHOD = "codexhost/harness/inspect";
17702
18237
  var THREAD_FORK_METHOD = "codexhost/thread/fork";
17703
18238
  var THREAD_INSPECT_METHOD = "codexhost/thread/inspect";
18239
+ var THREAD_COMMANDS_INSPECT_METHOD = "codexhost/thread/commands/inspect";
18240
+ var THREAD_COMMAND_EXECUTE_METHOD = "codexhost/thread/command/execute";
17704
18241
  var THREAD_MODEL_SELECT_METHOD = "codexhost/thread/model/select";
17705
18242
  var THREAD_THINKING_SELECT_METHOD = "codexhost/thread/thinking/select";
17706
18243
  var THREAD_PERMISSION_MODE_SELECT_METHOD = "codexhost/thread/permission-mode/select";
@@ -17722,6 +18259,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17722
18259
  const parsed = hostThreadIdSchema.safeParse(params.threadId);
17723
18260
  return parsed.success ? parsed.data : null;
17724
18261
  }
18262
+ function usageNotificationTarget(manager) {
18263
+ if (typeof manager.addNotificationCallback === "function") return manager;
18264
+ const nested = manager.requestClient;
18265
+ return nested && typeof nested.addNotificationCallback === "function" ? nested : null;
18266
+ }
17725
18267
  function createThreadUsageSubscriptionRelay() {
17726
18268
  const listeners = /* @__PURE__ */ new Set();
17727
18269
  let removeNotificationCallback = null;
@@ -17763,6 +18305,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17763
18305
  const result = await manager.sendRequest(HARNESS_INSPECT_METHOD, params);
17764
18306
  return harnessInspectionSchema.parse(result);
17765
18307
  };
18308
+ const inspectThreadCommands = async (input) => {
18309
+ const params = threadCommandsInspectParamsSchema.parse(input);
18310
+ const result = await manager.sendRequest(THREAD_COMMANDS_INSPECT_METHOD, params);
18311
+ return harnessCommandCatalogSchema.parse(result);
18312
+ };
18313
+ const executeThreadCommand = async (input) => {
18314
+ const params = threadCommandExecuteParamsSchema.parse(input);
18315
+ const result = await manager.sendRequest(THREAD_COMMAND_EXECUTE_METHOD, params);
18316
+ return threadCommandExecuteResultSchema.parse(result);
18317
+ };
17766
18318
  const inspectThreadUsage = async (input) => {
17767
18319
  const params = threadUsageInspectionParamsSchema.parse(input);
17768
18320
  const result = await manager.sendRequest(THREAD_USAGE_INSPECT_METHOD, params);
@@ -17795,6 +18347,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17795
18347
  const result = await manager.sendRequest(THREAD_INSPECT_METHOD, params);
17796
18348
  return threadInspectionSchema.parse(result);
17797
18349
  },
18350
+ inspectThreadCommands,
18351
+ executeThreadCommand,
17798
18352
  async listThreadOwnership(input) {
17799
18353
  const params = threadOwnershipListParamsSchema.parse(input);
17800
18354
  const value = await manager.sendRequest(THREAD_OWNERSHIP_LIST_METHOD, params);
@@ -17806,10 +18360,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17806
18360
  },
17807
18361
  inspectThreadUsage,
17808
18362
  subscribeThreadUsage(listener) {
17809
- if (typeof manager.addNotificationCallback !== "function") return () => void 0;
18363
+ const notifications = usageNotificationTarget(manager);
18364
+ if (!notifications?.addNotificationCallback) {
18365
+ throw new Error("Renderer Usage notification callback is unavailable");
18366
+ }
17810
18367
  let disposed = false;
17811
18368
  const generations = /* @__PURE__ */ new Map();
17812
- const removeNotificationCallback = manager.addNotificationCallback(
18369
+ const removeNotificationCallback = notifications.addNotificationCallback(
17813
18370
  THREAD_TOKEN_USAGE_UPDATED_METHOD,
17814
18371
  (notification) => {
17815
18372
  const threadId = notifiedThreadId(notification);
@@ -17971,14 +18528,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17971
18528
  });
17972
18529
  return model.success ? { model: model.data } : null;
17973
18530
  }
17974
- function isPiTransportModelId(value) {
17975
- if (value === PI_TRANSPORT_MODEL_ID) return true;
17976
- if (typeof value !== "string" || !value.startsWith(PI_TRANSPORT_MODEL_PREFIX)) return false;
18531
+ function decodePiTransportModelId(value) {
18532
+ if (value === PI_TRANSPORT_MODEL_ID) return {};
18533
+ if (typeof value !== "string" || !value.startsWith(PI_TRANSPORT_MODEL_PREFIX)) return null;
17977
18534
  const components = value.slice(PI_TRANSPORT_MODEL_PREFIX.length).split("@");
17978
- if (components.length < 1 || components.length > 2) return false;
18535
+ if (components.length < 1 || components.length > 2) return null;
17979
18536
  const [modelId, thinkingOptionId] = components;
17980
- if (!harnessModelRefSchema.safeParse({ id: modelId }).success) return false;
17981
- return components.length === 1 || thinkingOptionId !== void 0 && harnessThinkingOptionIdSchema.safeParse(thinkingOptionId).success;
18537
+ if (components.length === 2 && !thinkingOptionId) return null;
18538
+ const model = harnessModelRefSchema.safeParse({ id: modelId });
18539
+ if (!model.success) return null;
18540
+ const thinking = thinkingOptionId ? harnessThinkingOptionIdSchema.safeParse(thinkingOptionId) : null;
18541
+ if (thinking && !thinking.success) return null;
18542
+ return {
18543
+ model: model.data,
18544
+ ...thinking?.success ? { thinkingOptionId: thinking.data } : {}
18545
+ };
17982
18546
  }
17983
18547
  function threadIdFromComposerModelTarget(target) {
17984
18548
  if (target?.[0] !== "conversation" || typeof target[1] !== "string" || target[1].trim().length === 0) {
@@ -18034,7 +18598,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18034
18598
  if (isActiveRequestManager(hookState) && matchesCurrentPrewarmSignature(hookState)) {
18035
18599
  targets.add(hookState);
18036
18600
  } else if (hasPrewarmMethod(requestClient) && matchesCurrentPrewarmSignature(requestClient)) {
18037
- targets.add(requestClient);
18601
+ targets.add(typeof hookState.sendRequest === "function" ? hookState : requestClient);
18038
18602
  }
18039
18603
  }
18040
18604
  hook = typeof hook.next === "object" && hook.next !== null ? hook.next : null;
@@ -18163,6 +18727,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18163
18727
  forkThread: (input) => currentModelClient().forkThread(input),
18164
18728
  inspectHarness: (input) => currentModelClient().inspectHarness(input),
18165
18729
  inspectThread: (input) => currentModelClient().inspectThread(input),
18730
+ inspectThreadCommands: (input) => currentModelClient().inspectThreadCommands(input),
18731
+ executeThreadCommand: (input) => currentModelClient().executeThreadCommand(input),
18166
18732
  inspectThreadUsage: (input) => currentModelClient().inspectThreadUsage(input),
18167
18733
  subscribeThreadUsage: (listener) => usageSubscription.subscribe(listener),
18168
18734
  listThreadOwnership: (input) => currentModelClient().listThreadOwnership(input),
@@ -18554,6 +19120,30 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18554
19120
  inDevelopment: "In development",
18555
19121
  notAvailable: "Not available",
18556
19122
  runtimeCapabilityNotInstalled: "This runtime capability is not installed yet.",
19123
+ connectionsDescription: "Inspect the adapter and each Agent runtime used by the picker. Failed checks keep their error details here.",
19124
+ connectionAdapter: "Renderer adapter",
19125
+ connectionReason: "Reason",
19126
+ connectionRefresh: "Run connection diagnostics",
19127
+ connectionRefreshing: "Running diagnostics...",
19128
+ connectionViewDetails: "View details",
19129
+ connectionCopyDetails: "Copy diagnostics",
19130
+ connectionCopyAll: "Copy all diagnostics",
19131
+ connectionCopied: "Copied",
19132
+ connectionCopyFailed: "Copy failed",
19133
+ connectionErrorCode: "Error code",
19134
+ connectionErrorMessage: "Error message",
19135
+ connectionRetryable: "Retryable",
19136
+ connectionFailureStage: "Failure stage",
19137
+ connectionDuration: "Duration",
19138
+ connectionDiagnostic: "Diagnostic",
19139
+ connectionNoRuntime: "The renderer request bridge is not available yet.",
19140
+ connectionStatusReady: "Ready",
19141
+ connectionStatusChecking: "Checking",
19142
+ connectionStatusNotInstalled: "Not installed",
19143
+ connectionStatusUnavailable: "Unavailable",
19144
+ connectionStatusError: "Error",
19145
+ connectionStatusInstalling: "Installing",
19146
+ connectionStatusUnsupported: "Unsupported",
18557
19147
  openSettings: "Open codexhost settings",
18558
19148
  settingsButtonTitle: "codexhost settings",
18559
19149
  settingsUnavailableTitle: "codexhost settings unavailable",
@@ -18598,6 +19188,30 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18598
19188
  inDevelopment: "\u5F00\u53D1\u4E2D",
18599
19189
  notAvailable: "\u6682\u4E0D\u53EF\u7528",
18600
19190
  runtimeCapabilityNotInstalled: "\u8FD0\u884C\u65F6\u5C1A\u672A\u5B89\u88C5\u8BE5\u9879\u80FD\u529B\uFF0C\u56E0\u6B64\u6682\u4E0D\u53EF\u7528\u3002",
19191
+ 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
+ connectionAdapter: "Renderer \u9002\u914D\u5668",
19193
+ connectionReason: "\u539F\u56E0",
19194
+ connectionRefresh: "\u91CD\u65B0\u8BCA\u65AD\u8FDE\u63A5",
19195
+ connectionRefreshing: "\u6B63\u5728\u8BCA\u65AD...",
19196
+ connectionViewDetails: "\u67E5\u770B\u8BE6\u60C5",
19197
+ connectionCopyDetails: "\u590D\u5236\u8BCA\u65AD\u4FE1\u606F",
19198
+ connectionCopyAll: "\u590D\u5236\u5168\u90E8\u8BCA\u65AD\u4FE1\u606F",
19199
+ connectionCopied: "\u5DF2\u590D\u5236",
19200
+ connectionCopyFailed: "\u590D\u5236\u5931\u8D25",
19201
+ connectionErrorCode: "\u9519\u8BEF\u7801",
19202
+ connectionErrorMessage: "\u9519\u8BEF\u4FE1\u606F",
19203
+ connectionRetryable: "\u53EF\u91CD\u8BD5",
19204
+ connectionFailureStage: "\u5931\u8D25\u9636\u6BB5",
19205
+ connectionDuration: "\u68C0\u67E5\u8017\u65F6",
19206
+ connectionDiagnostic: "\u8BCA\u65AD\u4FE1\u606F",
19207
+ connectionNoRuntime: "Renderer \u8BF7\u6C42\u6865\u5C1A\u672A\u53EF\u7528\u3002",
19208
+ connectionStatusReady: "\u6B63\u5E38",
19209
+ connectionStatusChecking: "\u68C0\u67E5\u4E2D",
19210
+ connectionStatusNotInstalled: "\u672A\u5B89\u88C5",
19211
+ connectionStatusUnavailable: "\u4E0D\u53EF\u7528",
19212
+ connectionStatusError: "\u9519\u8BEF",
19213
+ connectionStatusInstalling: "\u5B89\u88C5\u4E2D",
19214
+ connectionStatusUnsupported: "\u4E0D\u652F\u6301",
18601
19215
  openSettings: "\u6253\u5F00 codexhost \u8BBE\u7F6E",
18602
19216
  settingsButtonTitle: "codexhost \u8BBE\u7F6E",
18603
19217
  settingsUnavailableTitle: "codexhost \u8BBE\u7F6E\u4E0D\u53EF\u7528",
@@ -18690,6 +19304,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18690
19304
  ["path", { d: "M19.08 19.08A10 10 0 1 1 4.92 4.92" }]
18691
19305
  ];
18692
19306
 
19307
+ // ../../node_modules/lucide/dist/esm/icons/copy.mjs
19308
+ var Copy = [
19309
+ ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }],
19310
+ ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" }]
19311
+ ];
19312
+
18693
19313
  // ../../node_modules/lucide/dist/esm/icons/download.mjs
18694
19314
  var Download = [
18695
19315
  ["path", { d: "M12 15V3" }],
@@ -18758,6 +19378,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18758
19378
  ["circle", { cx: "12", cy: "12", r: "3" }]
18759
19379
  ];
18760
19380
 
19381
+ // ../../node_modules/lucide/dist/esm/icons/stethoscope.mjs
19382
+ var Stethoscope = [
19383
+ ["path", { d: "M11 2v2" }],
19384
+ ["path", { d: "M5 2v2" }],
19385
+ ["path", { d: "M5 3H4a2 2 0 0 0-2 2v4a6 6 0 0 0 12 0V5a2 2 0 0 0-2-2h-1" }],
19386
+ ["path", { d: "M8 15a6 6 0 0 0 12 0v-3" }],
19387
+ ["circle", { cx: "20", cy: "10", r: "2" }]
19388
+ ];
19389
+
18761
19390
  // ../../node_modules/lucide/dist/esm/icons/star.mjs
18762
19391
  var Star = [
18763
19392
  [
@@ -18790,7 +19419,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18790
19419
  "updates",
18791
19420
  "external-link",
18792
19421
  "refresh",
18793
- "unavailable"
19422
+ "unavailable",
19423
+ "diagnose",
19424
+ "copy"
18794
19425
  ];
18795
19426
  var iconNodes = {
18796
19427
  settings: Settings,
@@ -18804,7 +19435,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18804
19435
  updates: Download,
18805
19436
  "external-link": ExternalLink,
18806
19437
  refresh: RefreshCw,
18807
- unavailable: CircleOff
19438
+ unavailable: CircleOff,
19439
+ diagnose: Stethoscope,
19440
+ copy: Copy
18808
19441
  };
18809
19442
  function isRendererSettingsIconName(value) {
18810
19443
  return RENDERER_SETTINGS_ICON_NAMES.includes(value);
@@ -19186,6 +19819,268 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19186
19819
  }
19187
19820
  });
19188
19821
  }
19822
+ function connectionStatusLabel(availability, messages) {
19823
+ if (availability === "ready") return messages.connectionStatusReady;
19824
+ if (availability === "checking") return messages.connectionStatusChecking;
19825
+ if (availability === "notInstalled") return messages.connectionStatusNotInstalled;
19826
+ if (availability === "unavailable" || availability === "error") {
19827
+ return availability === "error" ? messages.connectionStatusError : messages.connectionStatusUnavailable;
19828
+ }
19829
+ return availability === "installing" ? messages.connectionStatusInstalling : messages.connectionStatusUnsupported;
19830
+ }
19831
+ function connectionStatusTone(availability) {
19832
+ if (availability === "ready") return "ready";
19833
+ if (availability === "checking" || availability === "installing") return "checking";
19834
+ return "failed";
19835
+ }
19836
+ function diagnosticText(name, snapshot) {
19837
+ const error51 = snapshot.error;
19838
+ const lines = [
19839
+ "codexhost connection diagnostics",
19840
+ `agent: ${name}`,
19841
+ `status: ${snapshot.availability}`,
19842
+ ...error51 ? [
19843
+ `error.code: ${error51.code}`,
19844
+ `error.message: ${error51.message}`,
19845
+ `retryable: ${error51.retryable}`,
19846
+ ...error51.stage ? [`stage: ${error51.stage}`] : [],
19847
+ ...error51.durationMs !== void 0 ? [`durationMs: ${error51.durationMs}`] : [],
19848
+ ...error51.diagnostic ? [`diagnostic: ${error51.diagnostic}`] : [],
19849
+ ...error51.stderrTail ? [`stderr:
19850
+ ${error51.stderrTail}`] : []
19851
+ ] : []
19852
+ ];
19853
+ return lines.join("\n");
19854
+ }
19855
+ function detailLine(document2, label, value) {
19856
+ const line = document2.createElement("div");
19857
+ line.className = "settings-connection-detail-line";
19858
+ const name = document2.createElement("span");
19859
+ name.textContent = label;
19860
+ const content = document2.createElement("code");
19861
+ content.textContent = value;
19862
+ line.append(name, content);
19863
+ return line;
19864
+ }
19865
+ function setCopyButtonLabel(button, label) {
19866
+ button.replaceChildren(createRendererSettingsIcon("copy", 16), label);
19867
+ }
19868
+ function showCopyButtonFeedback(button, label, restoreLabel) {
19869
+ setCopyButtonLabel(button, label);
19870
+ button.ownerDocument.defaultView?.setTimeout(() => {
19871
+ setCopyButtonLabel(button, restoreLabel);
19872
+ }, 2e3);
19873
+ }
19874
+ function copyDiagnosticsToClipboard(document2, button, report, messages, restoreLabel) {
19875
+ const clipboard = document2.defaultView?.navigator.clipboard;
19876
+ if (!clipboard) {
19877
+ showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel);
19878
+ return;
19879
+ }
19880
+ void clipboard.writeText(report).then(
19881
+ () => showCopyButtonFeedback(button, messages.connectionCopied, restoreLabel),
19882
+ () => showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel)
19883
+ );
19884
+ }
19885
+ function appendConnectionRow(document2, parent, name, availability, detail, error51, messages, rowAttribute, diagnosticSnapshot) {
19886
+ const row = document2.createElement("div");
19887
+ row.className = "settings-connection-row";
19888
+ if (rowAttribute) row.dataset.connectionAgent = rowAttribute;
19889
+ const identity = document2.createElement("div");
19890
+ identity.className = "settings-connection-row__identity";
19891
+ const dot = document2.createElement("span");
19892
+ dot.className = "settings-connection-row__dot";
19893
+ dot.dataset.connectionTone = connectionStatusTone(availability);
19894
+ dot.setAttribute("aria-hidden", "true");
19895
+ const label = document2.createElement("strong");
19896
+ label.textContent = name;
19897
+ identity.append(dot, label);
19898
+ const status = document2.createElement("span");
19899
+ status.className = "settings-status-badge";
19900
+ status.dataset.connectionTone = connectionStatusTone(availability);
19901
+ status.textContent = connectionStatusLabel(availability, messages);
19902
+ const detailElement = document2.createElement("div");
19903
+ detailElement.className = "settings-connection-row__detail";
19904
+ const summary = document2.createElement("span");
19905
+ summary.className = "settings-connection-row__reason";
19906
+ summary.textContent = error51 ? `${messages.connectionReason}: ${error51.code}: ${error51.message}` : detail ? `${messages.connectionReason}: ${detail}` : "";
19907
+ detailElement.append(summary);
19908
+ let toggle = null;
19909
+ if (error51) {
19910
+ toggle = document2.createElement("button");
19911
+ toggle.type = "button";
19912
+ toggle.className = "settings-connection-details-toggle";
19913
+ toggle.textContent = messages.connectionViewDetails;
19914
+ toggle.setAttribute("aria-expanded", "true");
19915
+ detailElement.append(toggle);
19916
+ }
19917
+ const details = document2.createElement("div");
19918
+ details.className = "settings-connection-details";
19919
+ details.hidden = !error51;
19920
+ if (error51) {
19921
+ details.append(
19922
+ detailLine(document2, messages.connectionErrorCode, error51.code),
19923
+ detailLine(document2, messages.connectionErrorMessage, error51.message),
19924
+ detailLine(document2, messages.connectionRetryable, String(error51.retryable))
19925
+ );
19926
+ if (error51.stage)
19927
+ details.append(detailLine(document2, messages.connectionFailureStage, error51.stage));
19928
+ if (error51.durationMs !== void 0) {
19929
+ details.append(detailLine(document2, messages.connectionDuration, `${error51.durationMs} ms`));
19930
+ }
19931
+ if (error51.diagnostic)
19932
+ details.append(detailLine(document2, messages.connectionDiagnostic, error51.diagnostic));
19933
+ if (error51.stderrTail) {
19934
+ const stderr = document2.createElement("pre");
19935
+ stderr.className = "settings-connection-stderr";
19936
+ stderr.textContent = error51.stderrTail;
19937
+ details.append(stderr);
19938
+ }
19939
+ const copy = document2.createElement("button");
19940
+ copy.type = "button";
19941
+ copy.className = "settings-command-button settings-command-button--secondary";
19942
+ setCopyButtonLabel(copy, messages.connectionCopyDetails);
19943
+ copy.addEventListener("click", () => {
19944
+ if (!diagnosticSnapshot) return;
19945
+ copyDiagnosticsToClipboard(
19946
+ document2,
19947
+ copy,
19948
+ diagnosticText(name, diagnosticSnapshot),
19949
+ messages,
19950
+ messages.connectionCopyDetails
19951
+ );
19952
+ });
19953
+ details.append(copy);
19954
+ }
19955
+ if (toggle) {
19956
+ toggle.addEventListener("click", () => {
19957
+ details.hidden = !details.hidden;
19958
+ toggle?.setAttribute("aria-expanded", String(!details.hidden));
19959
+ });
19960
+ }
19961
+ row.append(identity, status, detailElement, details);
19962
+ parent.append(row);
19963
+ }
19964
+ function connectionsPage(messages, getDiagnostics) {
19965
+ return Object.freeze({
19966
+ id: "connections",
19967
+ label: messages.pageLabels.connections,
19968
+ icon: "connections",
19969
+ mount(context) {
19970
+ const document2 = context.content.ownerDocument;
19971
+ const heading = document2.createElement("div");
19972
+ heading.className = "settings-section-label";
19973
+ heading.textContent = messages.pageLabels.connections;
19974
+ const description = document2.createElement("p");
19975
+ description.className = "settings-page-description";
19976
+ description.textContent = messages.connectionsDescription;
19977
+ const actions = document2.createElement("div");
19978
+ actions.className = "settings-connection-actions";
19979
+ const refresh = document2.createElement("button");
19980
+ refresh.type = "button";
19981
+ refresh.className = "settings-command-button settings-command-button--secondary";
19982
+ refresh.dataset.connectionAction = "refresh";
19983
+ refresh.append(createRendererSettingsIcon("diagnose", 16), messages.connectionRefresh);
19984
+ const copyAll = document2.createElement("button");
19985
+ copyAll.type = "button";
19986
+ copyAll.className = "settings-command-button settings-command-button--secondary";
19987
+ copyAll.dataset.connectionAction = "copy-all";
19988
+ copyAll.append(createRendererSettingsIcon("copy", 16), messages.connectionCopyAll);
19989
+ actions.append(refresh, copyAll);
19990
+ const content = document2.createElement("div");
19991
+ content.className = "settings-connection-list";
19992
+ context.content.append(heading, description, actions, content);
19993
+ let pending = false;
19994
+ const render = (snapshot) => {
19995
+ content.replaceChildren();
19996
+ if (!snapshot) {
19997
+ const empty = document2.createElement("div");
19998
+ empty.className = "settings-empty";
19999
+ empty.textContent = messages.connectionNoRuntime;
20000
+ content.append(empty);
20001
+ return;
20002
+ }
20003
+ appendConnectionRow(
20004
+ document2,
20005
+ content,
20006
+ messages.connectionAdapter,
20007
+ snapshot.adapter.state,
20008
+ `reason=${snapshot.adapter.reason}, hook=${snapshot.adapter.hook ?? "none"}`,
20009
+ null,
20010
+ messages,
20011
+ "renderer-adapter"
20012
+ );
20013
+ for (const agent of snapshot.agents) {
20014
+ appendConnectionRow(
20015
+ document2,
20016
+ content,
20017
+ RENDERER_AGENT_LABELS[agent.agent],
20018
+ agent.availability,
20019
+ null,
20020
+ agent.error,
20021
+ messages,
20022
+ agent.agent,
20023
+ agent
20024
+ );
20025
+ }
20026
+ };
20027
+ const diagnostics = getDiagnostics();
20028
+ render(diagnostics?.snapshot() ?? null);
20029
+ if (!diagnostics) {
20030
+ copyAll.disabled = true;
20031
+ return void 0;
20032
+ }
20033
+ copyAll.addEventListener("click", () => {
20034
+ const snapshot = diagnostics.snapshot();
20035
+ const report = snapshot.agents.map((agent) => diagnosticText(RENDERER_AGENT_LABELS[agent.agent], agent)).join("\n\n");
20036
+ copyDiagnosticsToClipboard(document2, copyAll, report, messages, messages.connectionCopyAll);
20037
+ });
20038
+ const unsubscribe = diagnostics.subscribe(() => render(diagnostics.snapshot()));
20039
+ refresh.addEventListener("click", () => {
20040
+ if (pending) return;
20041
+ pending = true;
20042
+ refresh.disabled = true;
20043
+ refresh.replaceChildren(
20044
+ createRendererSettingsIcon("diagnose", 16),
20045
+ messages.connectionRefreshing
20046
+ );
20047
+ void context.runLatest(() => diagnostics.refresh(), {
20048
+ success() {
20049
+ pending = false;
20050
+ refresh.disabled = false;
20051
+ refresh.replaceChildren(
20052
+ createRendererSettingsIcon("diagnose", 16),
20053
+ messages.connectionRefresh
20054
+ );
20055
+ render(diagnostics.snapshot());
20056
+ },
20057
+ failure(error51) {
20058
+ pending = false;
20059
+ refresh.disabled = false;
20060
+ refresh.replaceChildren(
20061
+ createRendererSettingsIcon("diagnose", 16),
20062
+ messages.connectionRefresh
20063
+ );
20064
+ 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
+ }
20075
+ }))
20076
+ });
20077
+ }
20078
+ });
20079
+ });
20080
+ return unsubscribe;
20081
+ }
20082
+ });
20083
+ }
19189
20084
  function versionRow(context, label, version2) {
19190
20085
  const row = context.content.ownerDocument.createElement("div");
19191
20086
  row.className = "settings-update-version-row";
@@ -19459,23 +20354,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19459
20354
  }
19460
20355
  });
19461
20356
  }
19462
- function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null) {
20357
+ function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null) {
19463
20358
  const unavailableIds = DEFAULT_RENDERER_SETTINGS_PAGE_IDS.filter(
19464
- (id) => id !== "updates"
20359
+ (id) => id !== "updates" && id !== "connections"
19465
20360
  );
19466
20361
  return Object.freeze([
20362
+ connectionsPage(messages, getDiagnostics),
19467
20363
  ...unavailableIds.map((id) => unavailablePage(id, messages)),
19468
20364
  updatesPage(messages, getUpdateClient)
19469
20365
  ]);
19470
20366
  }
19471
- function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null) {
20367
+ function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null) {
19472
20368
  return createRendererSettingsPageRegistry(
19473
- createDefaultRendererSettingsPages(messages, getUpdateClient)
20369
+ createDefaultRendererSettingsPages(messages, getUpdateClient, getDiagnostics)
19474
20370
  );
19475
20371
  }
19476
20372
 
19477
20373
  // src/settings/shell.css
19478
- 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-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-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';
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';
19479
20375
 
19480
20376
  // src/settings/shell.ts
19481
20377
  var SETTINGS_SHELL_ATTRIBUTE = "data-codexhost-settings-shell";
@@ -19945,7 +20841,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19945
20841
  const messages = rendererSettingsMessages(locale);
19946
20842
  const definitions = createDefaultRendererSettingsPages(
19947
20843
  messages,
19948
- options.getUpdateClient ?? (() => null)
20844
+ options.getUpdateClient ?? (() => null),
20845
+ options.getConnectionDiagnostics ?? (() => null)
19949
20846
  );
19950
20847
  const nextShell = installRendererSettingsShell(definitions, messages, ownerWindow.document);
19951
20848
  const nextTrigger = installRendererSettingsHeaderTrigger({
@@ -20140,14 +21037,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20140
21037
  function restoredThreadOwnership(inspection) {
20141
21038
  if (inspection.owner === "codex") return { agent: "codex" };
20142
21039
  if (inspection.harnessId === "pi") {
20143
- if (!isPiTransportModelId(inspection.transportModelId)) {
21040
+ const transportSelection = decodePiTransportModelId(inspection.transportModelId);
21041
+ if (!transportSelection) {
20144
21042
  throw new Error("Pi Thread reported an incompatible transport Model");
20145
21043
  }
20146
- const piThinkingOptionId = selectableThinkingOptionId(inspection);
21044
+ const model = inspection.effectiveModel ?? transportSelection.model;
21045
+ const thinkingOptionId = selectableThinkingOptionId(inspection) ?? transportSelection.thinkingOptionId;
20147
21046
  return {
20148
21047
  agent: "pi",
20149
- ...inspection.effectiveModel ? { model: inspection.effectiveModel } : {},
20150
- ...piThinkingOptionId ? { thinkingOptionId: piThinkingOptionId } : {},
21048
+ ...model ? { model } : {},
21049
+ ...thinkingOptionId ? { thinkingOptionId } : {},
20151
21050
  ...inspection.effectivePermissionModeId ? { permissionModeId: inspection.effectivePermissionModeId } : {}
20152
21051
  };
20153
21052
  }
@@ -20275,8 +21174,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20275
21174
  getClient: () => modelControl,
20276
21175
  getLocalAgent: localAgentForSidebarThread
20277
21176
  });
21177
+ let connectionDiagnostics = null;
20278
21178
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
20279
- getUpdateClient: () => modelControl
21179
+ getUpdateClient: () => modelControl,
21180
+ getConnectionDiagnostics: () => connectionDiagnostics
20280
21181
  });
20281
21182
  let adapterStatus = {
20282
21183
  state: "installing",
@@ -20287,6 +21188,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20287
21188
  let harnessAvailability = Object.fromEntries(
20288
21189
  externalAgents.map((agent) => [agent, "checking"])
20289
21190
  );
21191
+ const harnessAvailabilityErrors = {
21192
+ pi: void 0,
21193
+ "claude-code": void 0,
21194
+ "deepseek-harness": void 0,
21195
+ grok: void 0
21196
+ };
21197
+ const connectionListeners = /* @__PURE__ */ new Set();
21198
+ const publishConnectionStatus = () => {
21199
+ for (const listener of connectionListeners) listener();
21200
+ };
20290
21201
  let availabilityRequestGeneration = 0;
20291
21202
  let availabilityRequest = null;
20292
21203
  let availabilityRetryTimer = null;
@@ -20333,6 +21244,40 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20333
21244
  mounted.accountCredits
20334
21245
  );
20335
21246
  };
21247
+ const refreshCommands = async (mounted) => {
21248
+ const state = controller.get(mounted.composer);
21249
+ const threadId = threadIdFromComposerModelTarget(mounted.modelTarget);
21250
+ if (state.agent === "codex" || !threadId || !modelControl) {
21251
+ mounted.control.harnessCommands.setCommands([]);
21252
+ return;
21253
+ }
21254
+ try {
21255
+ const catalog = await modelControl.inspectThreadCommands({ threadId });
21256
+ if (disposed || mountedByComposer.get(mounted.composer) !== mounted || threadIdFromComposerModelTarget(mounted.modelTarget) !== threadId || controller.get(mounted.composer).agent === "codex") {
21257
+ return;
21258
+ }
21259
+ mounted.control.harnessCommands.setCommands(catalog.commands);
21260
+ } catch {
21261
+ if (mountedByComposer.get(mounted.composer) === mounted) {
21262
+ mounted.control.harnessCommands.setCommands([]);
21263
+ }
21264
+ }
21265
+ };
21266
+ const executeCommand = async (mounted, command) => {
21267
+ const threadId = threadIdFromComposerModelTarget(mounted.modelTarget);
21268
+ if (!threadId || !modelControl || controller.get(mounted.composer).agent === "codex") return;
21269
+ mounted.control.harnessCommands.setExecuting(command.id);
21270
+ try {
21271
+ await modelControl.executeThreadCommand({ threadId, commandId: command.id });
21272
+ } catch (error51) {
21273
+ console.error(
21274
+ "codexhost Harness command failed",
21275
+ error51 instanceof Error ? error51.message : String(error51)
21276
+ );
21277
+ } finally {
21278
+ mounted.control.harnessCommands.setExecuting(null);
21279
+ }
21280
+ };
20336
21281
  const applyThreadUsageUpdate = (update) => {
20337
21282
  for (const mounted of mountedByComposer.values()) {
20338
21283
  if (threadIdFromComposerModelTarget(mounted.modelTarget) !== update.threadId) continue;
@@ -20464,6 +21409,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20464
21409
  } finally {
20465
21410
  if (isCurrentOwnershipRequest(mounted, generation)) {
20466
21411
  renderMounted(mounted);
21412
+ if (mounted.ownershipStatus !== "error") void refreshCommands(mounted);
20467
21413
  sidebarAgentIcons.refresh();
20468
21414
  if (mounted.ownershipStatus !== "error" && shouldRetryExternalThreadUsage(
20469
21415
  controller.get(mounted.composer).agent,
@@ -21070,6 +22016,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21070
22016
  harnessAvailability[agent] === "ready" ? "ready" : "checking"
21071
22017
  ])
21072
22018
  );
22019
+ publishConnectionStatus();
21073
22020
  for (const mounted of mountedByComposer.values()) renderMounted(mounted);
21074
22021
  const generation = ++availabilityRequestGeneration;
21075
22022
  const promise2 = (async () => {
@@ -21082,8 +22029,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21082
22029
  refresh
21083
22030
  });
21084
22031
  status = inspection.status === "ready" ? "ready" : inspection.status;
21085
- } catch {
22032
+ if (inspection.status === "ready") {
22033
+ harnessAvailabilityErrors[agent] = void 0;
22034
+ } else {
22035
+ const error51 = inspection.error;
22036
+ harnessAvailabilityErrors[agent] = {
22037
+ code: error51.code,
22038
+ message: error51.message,
22039
+ retryable: error51.retryable,
22040
+ ...error51.diagnostic ? { diagnostic: error51.diagnostic } : {},
22041
+ ...error51.stage ? { stage: error51.stage } : {},
22042
+ ...error51.durationMs !== void 0 ? { durationMs: error51.durationMs } : {},
22043
+ ...error51.stderrTail ? { stderrTail: error51.stderrTail } : {}
22044
+ };
22045
+ }
22046
+ } catch (error51) {
21086
22047
  status = "error";
22048
+ harnessAvailabilityErrors[agent] = {
22049
+ code: "internalError",
22050
+ message: error51 instanceof Error ? error51.message : String(error51),
22051
+ retryable: true,
22052
+ stage: "request"
22053
+ };
21087
22054
  }
21088
22055
  if (generation !== availabilityRequestGeneration || disposed) return;
21089
22056
  harnessAvailability = { ...harnessAvailability, [agent]: status };
@@ -21100,6 +22067,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21100
22067
  }
21101
22068
  renderMounted(mounted);
21102
22069
  }
22070
+ publishConnectionStatus();
21103
22071
  })
21104
22072
  );
21105
22073
  if (externalAgents.every((agent) => harnessAvailability[agent] === "ready")) {
@@ -21120,6 +22088,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21120
22088
  );
21121
22089
  return promise2;
21122
22090
  };
22091
+ connectionDiagnostics = {
22092
+ snapshot() {
22093
+ return {
22094
+ adapter: { ...adapterStatus },
22095
+ agents: externalAgents.map((agent) => ({
22096
+ agent,
22097
+ availability: harnessAvailability[agent] ?? "checking",
22098
+ error: harnessAvailabilityErrors[agent] ?? null
22099
+ }))
22100
+ };
22101
+ },
22102
+ refresh() {
22103
+ return refreshHarnessAvailability(true);
22104
+ },
22105
+ subscribe(listener) {
22106
+ connectionListeners.add(listener);
22107
+ return () => connectionListeners.delete(listener);
22108
+ }
22109
+ };
21123
22110
  const mount = (composer) => {
21124
22111
  if (mountedByComposer.has(composer) || !composer.isConnected || !composer.matches(CODEX_COMPOSER_SELECTOR)) {
21125
22112
  return;
@@ -21128,6 +22115,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21128
22115
  const sendButton = sendButtonWithin(composer) ?? allButtons.at(-1) ?? null;
21129
22116
  if (!sendButton) return;
21130
22117
  const modelTarget = findComposerModelTarget(composer);
22118
+ const editor = composer.querySelector(EDITOR_SELECTOR);
22119
+ if (!editor) return;
21131
22120
  const state = controller.mount(
21132
22121
  composer,
21133
22122
  modelTarget,
@@ -21159,6 +22148,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21159
22148
  const mounted2 = mountedByComposer.get(composer);
21160
22149
  if (!composer.isConnected || !mounted2) return;
21161
22150
  void selectPermissionMode(mounted2, permissionModeId);
22151
+ },
22152
+ (command) => {
22153
+ const mounted2 = mountedByComposer.get(composer);
22154
+ if (mounted2) void executeCommand(mounted2, command);
21162
22155
  }
21163
22156
  );
21164
22157
  const mounted = {
@@ -21193,6 +22186,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21193
22186
  } else if (state.agent !== "codex" && !isExternalConfigurationReady(mounted)) {
21194
22187
  void loadExternalCatalog(mounted);
21195
22188
  }
22189
+ void refreshCommands(mounted);
21196
22190
  };
21197
22191
  const scan = () => {
21198
22192
  scanScheduled = false;
@@ -21385,6 +22379,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21385
22379
  scheduleScan(mutations.some(mutationMayChangeComposerTarget));
21386
22380
  });
21387
22381
  const onAdapterStatus = () => {
22382
+ publishConnectionStatus();
21388
22383
  if (adapterStatus.state === "ready") {
21389
22384
  sidebarAgentIcons.refresh();
21390
22385
  void refreshHarnessAvailability();
@@ -21466,6 +22461,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21466
22461
  usageNotificationDispose = null;
21467
22462
  }
21468
22463
  adapterStatus = status;
22464
+ publishConnectionStatus();
21469
22465
  const installedModelControl = modelControl;
21470
22466
  queueMicrotask(() => {
21471
22467
  if (disposed || modelControl !== installedModelControl) return;
@@ -21520,6 +22516,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21520
22516
  }
21521
22517
  mountedByComposer.clear();
21522
22518
  pendingReplacements.clear();
22519
+ connectionListeners.clear();
22520
+ connectionDiagnostics = null;
21523
22521
  delete window.__codexhostRendererBindingProbeV1;
21524
22522
  }
21525
22523
  };
@@ -21565,6 +22563,7 @@ lucide/dist/esm/icons/shield.mjs:
21565
22563
  lucide/dist/esm/icons/shield-alert.mjs:
21566
22564
  lucide/dist/esm/icons/boxes.mjs:
21567
22565
  lucide/dist/esm/icons/circle-off.mjs:
22566
+ lucide/dist/esm/icons/copy.mjs:
21568
22567
  lucide/dist/esm/icons/download.mjs:
21569
22568
  lucide/dist/esm/icons/external-link.mjs:
21570
22569
  lucide/dist/esm/icons/languages.mjs:
@@ -21573,6 +22572,7 @@ lucide/dist/esm/icons/plug-zap.mjs:
21573
22572
  lucide/dist/esm/icons/refresh-cw.mjs:
21574
22573
  lucide/dist/esm/icons/route.mjs:
21575
22574
  lucide/dist/esm/icons/settings.mjs:
22575
+ lucide/dist/esm/icons/stethoscope.mjs:
21576
22576
  lucide/dist/esm/icons/star.mjs:
21577
22577
  lucide/dist/esm/icons/x.mjs:
21578
22578
  (**