@codexhost/cli-linux-x64 0.3.0-test.1 → 0.3.0-test.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18032,6 +18032,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18032
18032
  isConnected() {
18033
18033
  return this.element.isConnected;
18034
18034
  }
18035
+ hostId() {
18036
+ return sidebarThreadAttributes(this.element)?.hostId ?? null;
18037
+ }
18035
18038
  threadId() {
18036
18039
  return threadIdFromSidebarRowElement(this.element);
18037
18040
  }
@@ -18101,7 +18104,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18101
18104
  const observer = new MutationObserver(onChange);
18102
18105
  observer.observe(this.root, {
18103
18106
  attributes: true,
18104
- attributeFilter: [SIDEBAR_THREAD_ID_ATTRIBUTE],
18107
+ attributeFilter: [SIDEBAR_THREAD_ID_ATTRIBUTE, SIDEBAR_THREAD_HOST_ID_ATTRIBUTE],
18105
18108
  childList: true,
18106
18109
  subtree: true
18107
18110
  });
@@ -18122,78 +18125,88 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18122
18125
  const ownershipRetryTimers = /* @__PURE__ */ new Map();
18123
18126
  let disposed = false;
18124
18127
  let scanScheduled = false;
18128
+ const ownershipKey = (hostId, threadId) => JSON.stringify([hostId, threadId]);
18125
18129
  const scheduleScan = () => {
18126
18130
  if (disposed || scanScheduled) return;
18127
18131
  scanScheduled = true;
18128
18132
  queueMicrotask(scan);
18129
18133
  };
18130
- const clearOwnershipRetry = (threadId) => {
18131
- const timer = ownershipRetryTimers.get(threadId);
18134
+ const clearOwnershipRetry = (key) => {
18135
+ const timer = ownershipRetryTimers.get(key);
18132
18136
  if (timer !== void 0) clearTimeout(timer);
18133
- ownershipRetryTimers.delete(threadId);
18134
- ownershipRetryAttempts.delete(threadId);
18135
- provisionalCodex.delete(threadId);
18137
+ ownershipRetryTimers.delete(key);
18138
+ ownershipRetryAttempts.delete(key);
18139
+ provisionalCodex.delete(key);
18136
18140
  };
18137
- const scheduleOwnershipRetry = (threadId) => {
18138
- if (disposed || !provisionalCodex.has(threadId) || pending.has(threadId) || ownershipRetryTimers.has(threadId)) {
18141
+ const scheduleOwnershipRetry = (hostId, threadId) => {
18142
+ const key = ownershipKey(hostId, threadId);
18143
+ if (disposed || !provisionalCodex.has(key) || pending.has(key) || ownershipRetryTimers.has(key)) {
18139
18144
  return;
18140
18145
  }
18141
- const attempt = ownershipRetryAttempts.get(threadId) ?? 0;
18146
+ const attempt = ownershipRetryAttempts.get(key) ?? 0;
18142
18147
  const delay = OWNERSHIP_RETRY_DELAYS_MS[attempt];
18143
18148
  if (delay === void 0) return;
18144
- ownershipRetryAttempts.set(threadId, attempt + 1);
18149
+ ownershipRetryAttempts.set(key, attempt + 1);
18145
18150
  const timer = setTimeout(() => {
18146
- ownershipRetryTimers.delete(threadId);
18151
+ ownershipRetryTimers.delete(key);
18147
18152
  if (disposed) return;
18148
- failed.delete(threadId);
18149
- ownershipByThread.delete(threadId);
18153
+ failed.delete(key);
18154
+ ownershipByThread.delete(key);
18150
18155
  scheduleScan();
18151
18156
  }, delay);
18152
- ownershipRetryTimers.set(threadId, timer);
18157
+ ownershipRetryTimers.set(key, timer);
18153
18158
  };
18154
- const requestOwnership = (threadIds, client) => {
18155
- for (const threadId of threadIds) pending.add(threadId);
18159
+ const requestOwnership = (hostId, threadIds, client) => {
18160
+ for (const threadId of threadIds) pending.add(ownershipKey(hostId, threadId));
18156
18161
  let succeeded = false;
18157
18162
  void Promise.resolve().then(() => client.listThreadOwnership({ threadIds })).then(({ threads }) => {
18158
18163
  if (disposed) return;
18159
18164
  for (const ownership of threads) {
18160
- ownershipByThread.set(ownership.threadId, rendererAgentForThreadOwnership(ownership));
18161
- failed.delete(ownership.threadId);
18165
+ const key = ownershipKey(hostId, ownership.threadId);
18166
+ ownershipByThread.set(key, rendererAgentForThreadOwnership(ownership));
18167
+ failed.delete(key);
18162
18168
  if (ownership.owner === "codex") {
18163
- provisionalCodex.add(ownership.threadId);
18164
- scheduleOwnershipRetry(ownership.threadId);
18169
+ provisionalCodex.add(key);
18170
+ scheduleOwnershipRetry(hostId, ownership.threadId);
18165
18171
  } else {
18166
- clearOwnershipRetry(ownership.threadId);
18172
+ clearOwnershipRetry(key);
18167
18173
  }
18168
18174
  }
18169
18175
  succeeded = true;
18170
18176
  }).catch(() => {
18171
18177
  if (disposed) return;
18172
- for (const threadId of threadIds) failed.add(threadId);
18178
+ for (const threadId of threadIds) failed.add(ownershipKey(hostId, threadId));
18173
18179
  }).finally(() => {
18174
- for (const threadId of threadIds) pending.delete(threadId);
18175
- for (const threadId of threadIds) scheduleOwnershipRetry(threadId);
18180
+ for (const threadId of threadIds) pending.delete(ownershipKey(hostId, threadId));
18181
+ for (const threadId of threadIds) scheduleOwnershipRetry(hostId, threadId);
18176
18182
  if (succeeded) scheduleScan();
18177
18183
  });
18178
18184
  };
18179
18185
  const scan = () => {
18180
18186
  scanScheduled = false;
18181
18187
  if (disposed) return;
18182
- const unresolved = /* @__PURE__ */ new Set();
18188
+ const unresolvedByHost = /* @__PURE__ */ new Map();
18183
18189
  for (const row of dom.rows()) {
18184
18190
  if (!row.isConnected()) {
18185
18191
  row.clear();
18186
18192
  continue;
18187
18193
  }
18194
+ const hostId = row.hostId();
18195
+ if (!hostId) {
18196
+ row.clear();
18197
+ continue;
18198
+ }
18188
18199
  const threadId = hostThreadIdSchema.safeParse(row.threadId());
18189
18200
  const localAgent = options.getLocalAgent?.({
18201
+ hostId,
18190
18202
  threadId: threadId.success ? threadId.data : null,
18191
18203
  draftId: row.draftId()
18192
18204
  });
18193
18205
  if (localAgent !== null && localAgent !== void 0) {
18194
18206
  if (threadId.success) {
18195
- ownershipByThread.set(threadId.data, localAgent === "codex" ? null : localAgent);
18196
- clearOwnershipRetry(threadId.data);
18207
+ const key2 = ownershipKey(hostId, threadId.data);
18208
+ ownershipByThread.set(key2, localAgent === "codex" ? null : localAgent);
18209
+ clearOwnershipRetry(key2);
18197
18210
  }
18198
18211
  if (localAgent === "codex") row.clear();
18199
18212
  else row.render(localAgent);
@@ -18203,22 +18216,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18203
18216
  row.clear();
18204
18217
  continue;
18205
18218
  }
18206
- if (ownershipByThread.has(threadId.data)) {
18207
- const agent = ownershipByThread.get(threadId.data);
18219
+ const key = ownershipKey(hostId, threadId.data);
18220
+ if (ownershipByThread.has(key)) {
18221
+ const agent = ownershipByThread.get(key);
18208
18222
  if (agent) row.render(agent);
18209
18223
  else row.clear();
18210
18224
  continue;
18211
18225
  }
18212
18226
  row.clear();
18213
- if (!pending.has(threadId.data) && !failed.has(threadId.data)) {
18227
+ if (!pending.has(key) && !failed.has(key)) {
18228
+ let unresolved = unresolvedByHost.get(hostId);
18229
+ if (!unresolved) {
18230
+ unresolved = /* @__PURE__ */ new Set();
18231
+ unresolvedByHost.set(hostId, unresolved);
18232
+ }
18214
18233
  unresolved.add(threadId.data);
18215
18234
  }
18216
18235
  }
18217
- const client = options.getClient();
18218
- if (!client) return;
18219
- const threadIds = [...unresolved];
18220
- for (let index = 0; index < threadIds.length; index += THREAD_OWNERSHIP_LIST_MAX_LENGTH) {
18221
- requestOwnership(threadIds.slice(index, index + THREAD_OWNERSHIP_LIST_MAX_LENGTH), client);
18236
+ for (const [hostId, unresolved] of unresolvedByHost) {
18237
+ const client = options.getClient(hostId);
18238
+ if (!client) continue;
18239
+ const threadIds = [...unresolved];
18240
+ for (let index = 0; index < threadIds.length; index += THREAD_OWNERSHIP_LIST_MAX_LENGTH) {
18241
+ requestOwnership(
18242
+ hostId,
18243
+ threadIds.slice(index, index + THREAD_OWNERSHIP_LIST_MAX_LENGTH),
18244
+ client
18245
+ );
18246
+ }
18222
18247
  }
18223
18248
  };
18224
18249
  const stopObserving = dom.observe(scheduleScan);
@@ -18226,12 +18251,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18226
18251
  return {
18227
18252
  refresh() {
18228
18253
  failed.clear();
18229
- for (const threadId of provisionalCodex) {
18230
- const timer = ownershipRetryTimers.get(threadId);
18254
+ for (const key of provisionalCodex) {
18255
+ const timer = ownershipRetryTimers.get(key);
18231
18256
  if (timer !== void 0) clearTimeout(timer);
18232
- ownershipRetryTimers.delete(threadId);
18233
- ownershipRetryAttempts.delete(threadId);
18234
- ownershipByThread.delete(threadId);
18257
+ ownershipRetryTimers.delete(key);
18258
+ ownershipRetryAttempts.delete(key);
18259
+ ownershipByThread.delete(key);
18235
18260
  }
18236
18261
  scheduleScan();
18237
18262
  },
@@ -18722,22 +18747,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18722
18747
  }
18723
18748
  return hostThreadIdSchema.parse(target[1]);
18724
18749
  }
18725
- function hasPrewarmMethod(value) {
18726
- return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.prewarmThreadStart === "function";
18727
- }
18728
- function isActiveRequestManager(value) {
18729
- if (!isRecord5(value) || typeof value.sendRequest !== "function") return false;
18730
- const source = Function.prototype.toString.call(value.sendRequest);
18731
- return source.includes("send-cli-request-for-host") && hasPrewarmMethod(value.requestClient);
18732
- }
18733
- function matchesCurrentPrewarmSignature(target) {
18734
- const bridge = target.requestClient ?? target;
18735
- const stableApiShape = typeof bridge.hostId === "string" && bridge.hostId.length > 0 && typeof bridge.sendRequest === "function" && typeof bridge.prewarmThreadStart === "function" && typeof bridge.enqueueRequest === "function";
18736
- if (stableApiShape) return true;
18737
- const prewarm = target.prewarmThreadStart ?? target.requestClient?.prewarmThreadStart;
18738
- if (!prewarm) return false;
18739
- const source = Function.prototype.toString.call(prewarm);
18740
- return source.includes("enqueueRequest") && source.includes("thread-prewarm-start") || source.includes("prewarm-thread-start-for-host");
18750
+ function isCurrentRequestBridge(value) {
18751
+ return isRecord5(value) && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.sendRequest === "function" && typeof value.prewarmThreadStart === "function" && typeof value.enqueueRequest === "function";
18741
18752
  }
18742
18753
  function findActivePrewarmTargets(root) {
18743
18754
  const editor = root.querySelector(
@@ -18767,10 +18778,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18767
18778
  const hookState = hook.memoizedState;
18768
18779
  if (isRecord5(hookState)) {
18769
18780
  const requestClient = hookState.requestClient;
18770
- if (isActiveRequestManager(hookState) && matchesCurrentPrewarmSignature(hookState)) {
18771
- targets.add(hookState);
18772
- } else if (hasPrewarmMethod(requestClient) && matchesCurrentPrewarmSignature(requestClient)) {
18773
- targets.add(typeof hookState.sendRequest === "function" ? hookState : requestClient);
18781
+ const bridge = isCurrentRequestBridge(requestClient) ? requestClient : isCurrentRequestBridge(hookState) ? hookState : null;
18782
+ if (bridge) {
18783
+ targets.add(typeof hookState.sendRequest === "function" ? hookState : bridge);
18774
18784
  }
18775
18785
  }
18776
18786
  hook = typeof hook.next === "object" && hook.next !== null ? hook.next : null;
@@ -18872,13 +18882,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18872
18882
  function isDraftPrewarmPolicyReady(value) {
18873
18883
  return isRecord5(value) && value.state === "ready" && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.select === "function" && typeof value.clear === "function";
18874
18884
  }
18885
+ function prewarmTargetHostId(target) {
18886
+ const bridge = target.requestClient ?? target;
18887
+ const hostId = target.getHostId?.() ?? bridge.hostId;
18888
+ return typeof hostId === "string" && hostId.length > 0 ? hostId : null;
18889
+ }
18890
+ function rendererRequestTargetsForHost(targets, hostId) {
18891
+ const matching = targets.filter((target) => prewarmTargetHostId(target) === hostId);
18892
+ return matching.length === 1 ? matching : null;
18893
+ }
18875
18894
  function activeRendererDraftPrewarmTargets(policy, targets) {
18876
18895
  if (!isDraftPrewarmPolicyReady(policy)) return null;
18877
- const activeTargets = targets.filter((target) => {
18878
- const bridge = target.requestClient ?? target;
18879
- return (target.getHostId?.() ?? bridge.hostId) === policy.hostId;
18880
- });
18881
- return activeTargets.length === 1 ? activeTargets : null;
18896
+ return rendererRequestTargetsForHost(targets, policy.hostId);
18882
18897
  }
18883
18898
  function resolveRendererRequestRoute(policy, discoveredTargets, previous) {
18884
18899
  const activeTargets = activeRendererDraftPrewarmTargets(policy, discoveredTargets);
@@ -18943,13 +18958,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18943
18958
  () => findActivePrewarmTargets(document)
18944
18959
  );
18945
18960
  const currentRequestRoute = () => requestRouteResolver.resolve();
18961
+ const clientsByTarget = /* @__PURE__ */ new WeakMap();
18962
+ const modelClientForTargets = (targets) => {
18963
+ const target = targets[0];
18964
+ if (targets.length !== 1 || !target) return null;
18965
+ const cached2 = clientsByTarget.get(target);
18966
+ if (cached2) return cached2;
18967
+ const client = createRendererModelClient([target]);
18968
+ if (client) clientsByTarget.set(target, client);
18969
+ return client;
18970
+ };
18946
18971
  const currentModelClient = () => {
18947
- const client = createRendererModelClient(currentRequestRoute()?.targets ?? []);
18972
+ const client = modelClientForTargets(currentRequestRoute()?.targets ?? []);
18948
18973
  if (!client) throw new Error("Renderer Model request manager is unavailable");
18949
18974
  usageSubscription.connect(client);
18950
18975
  return client;
18951
18976
  };
18952
18977
  const modelControl = Object.freeze({
18978
+ currentHostId: () => currentRequestRoute()?.policy.hostId ?? null,
18979
+ clientForHost(hostId) {
18980
+ const targets = rendererRequestTargetsForHost(findActivePrewarmTargets(document), hostId);
18981
+ return modelClientForTargets(targets ?? []);
18982
+ },
18953
18983
  forkThread: (input) => currentModelClient().forkThread(input),
18954
18984
  inspectHarness: (input) => currentModelClient().inspectHarness(input),
18955
18985
  inspectThread: (input) => currentModelClient().inspectThread(input),
@@ -19368,6 +19398,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19368
19398
  runtimeCapabilityNotInstalled: "This runtime capability is not installed yet.",
19369
19399
  connectionsDescription: "Inspect the adapter and each Agent runtime used by the picker. Failed checks keep their error details here.",
19370
19400
  connectionAdapter: "Renderer adapter",
19401
+ connectionHosts: "Hosts",
19402
+ connectionLocalHost: "Local",
19403
+ connectionRemoteHost: "Remote Host",
19404
+ connectionActiveHost: "Current",
19371
19405
  connectionReason: "Reason",
19372
19406
  connectionRefresh: "Run connection diagnostics",
19373
19407
  connectionRefreshing: "Running diagnostics...",
@@ -19436,6 +19470,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19436
19470
  runtimeCapabilityNotInstalled: "\u8FD0\u884C\u65F6\u5C1A\u672A\u5B89\u88C5\u8BE5\u9879\u80FD\u529B\uFF0C\u56E0\u6B64\u6682\u4E0D\u53EF\u7528\u3002",
19437
19471
  connectionsDescription: "\u67E5\u770B\u9002\u914D\u5668\u548C Agent \u8FD0\u884C\u65F6\u7684\u771F\u5B9E\u68C0\u67E5\u7ED3\u679C\u3002\u5931\u8D25\u68C0\u67E5\u4F1A\u4FDD\u7559\u9519\u8BEF\u8BE6\u60C5\uFF0C\u65B9\u4FBF\u6392\u67E5\u65E0\u6CD5\u9009\u62E9\u7684\u95EE\u9898\u3002",
19438
19472
  connectionAdapter: "Renderer \u9002\u914D\u5668",
19473
+ connectionHosts: "Host \u5217\u8868",
19474
+ connectionLocalHost: "\u672C\u5730",
19475
+ connectionRemoteHost: "\u8FDC\u7A0B Host",
19476
+ connectionActiveHost: "\u5F53\u524D",
19439
19477
  connectionReason: "\u539F\u56E0",
19440
19478
  connectionRefresh: "\u91CD\u65B0\u8BCA\u65AD\u8FDE\u63A5",
19441
19479
  connectionRefreshing: "\u6B63\u5728\u8BCA\u65AD...",
@@ -20079,10 +20117,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20079
20117
  if (availability === "checking" || availability === "installing") return "checking";
20080
20118
  return "failed";
20081
20119
  }
20082
- function diagnosticText(name, snapshot) {
20120
+ function diagnosticText(hostId, name, snapshot) {
20083
20121
  const error51 = snapshot.error;
20084
20122
  const lines = [
20085
20123
  "codexhost connection diagnostics",
20124
+ `host: ${hostId}`,
20086
20125
  `agent: ${name}`,
20087
20126
  `status: ${snapshot.availability}`,
20088
20127
  ...error51 ? [
@@ -20128,7 +20167,7 @@ ${error51.stderrTail}`] : []
20128
20167
  () => showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel)
20129
20168
  );
20130
20169
  }
20131
- function appendConnectionRow(document2, parent, name, availability, detail, error51, messages, rowAttribute, diagnosticSnapshot) {
20170
+ function appendConnectionRow(document2, parent, name, availability, detail, error51, messages, rowAttribute, diagnosticSnapshot, diagnosticHostId) {
20132
20171
  const row = document2.createElement("div");
20133
20172
  row.className = "settings-connection-row";
20134
20173
  if (rowAttribute) row.dataset.connectionAgent = rowAttribute;
@@ -20191,7 +20230,7 @@ ${error51.stderrTail}`] : []
20191
20230
  copyDiagnosticsToClipboard(
20192
20231
  document2,
20193
20232
  copy,
20194
- diagnosticText(name, diagnosticSnapshot),
20233
+ diagnosticText(diagnosticHostId ?? "unknown", name, diagnosticSnapshot),
20195
20234
  messages,
20196
20235
  messages.connectionCopyDetails
20197
20236
  );
@@ -20207,6 +20246,18 @@ ${error51.stderrTail}`] : []
20207
20246
  row.append(identity, status, detailElement, details);
20208
20247
  parent.append(row);
20209
20248
  }
20249
+ function connectionHostLabel(hostId, active, messages) {
20250
+ const activeLabel = active ? ` \xB7 ${messages.connectionActiveHost}` : "";
20251
+ if (hostId === "local") return `${messages.connectionLocalHost}${activeLabel}`;
20252
+ const separator = hostId.lastIndexOf(":");
20253
+ const encodedName = separator >= 0 ? hostId.slice(separator + 1) : hostId;
20254
+ let name = encodedName;
20255
+ try {
20256
+ name = decodeURIComponent(encodedName);
20257
+ } catch {
20258
+ }
20259
+ return `${messages.connectionRemoteHost}: ${name}${activeLabel}`;
20260
+ }
20210
20261
  function connectionsPage(messages, getDiagnostics) {
20211
20262
  return Object.freeze({
20212
20263
  id: "connections",
@@ -20237,7 +20288,10 @@ ${error51.stderrTail}`] : []
20237
20288
  content.className = "settings-connection-list";
20238
20289
  context.content.append(heading, description, actions, content);
20239
20290
  let pending = false;
20291
+ let selectedHostId = "local";
20292
+ let latestSnapshot = null;
20240
20293
  const render = (snapshot) => {
20294
+ latestSnapshot = snapshot;
20241
20295
  content.replaceChildren();
20242
20296
  if (!snapshot) {
20243
20297
  const empty = document2.createElement("div");
@@ -20256,19 +20310,50 @@ ${error51.stderrTail}`] : []
20256
20310
  messages,
20257
20311
  "renderer-adapter"
20258
20312
  );
20259
- for (const agent of snapshot.agents) {
20313
+ const selectedHost = snapshot.hosts.find((host) => host.hostId === selectedHostId) ?? snapshot.hosts.find((host) => host.hostId === "local") ?? snapshot.hosts[0];
20314
+ if (!selectedHost) return;
20315
+ selectedHostId = selectedHost.hostId;
20316
+ const tabs = document2.createElement("div");
20317
+ tabs.className = "settings-connection-host-tabs";
20318
+ tabs.setAttribute("role", "tablist");
20319
+ tabs.setAttribute("aria-label", messages.connectionHosts);
20320
+ const hostSection = document2.createElement("section");
20321
+ hostSection.className = "settings-connection-host";
20322
+ hostSection.dataset.connectionHost = selectedHost.hostId;
20323
+ hostSection.setAttribute("role", "tabpanel");
20324
+ const panelId = "codexhost-settings-connection-host-panel";
20325
+ hostSection.id = panelId;
20326
+ for (const host of snapshot.hosts) {
20327
+ const tab = document2.createElement("button");
20328
+ tab.type = "button";
20329
+ tab.className = "settings-connection-host-tab";
20330
+ tab.dataset.connectionHostTab = host.hostId;
20331
+ tab.setAttribute("role", "tab");
20332
+ tab.setAttribute("aria-controls", panelId);
20333
+ tab.setAttribute("aria-selected", String(host.hostId === selectedHost.hostId));
20334
+ tab.tabIndex = host.hostId === selectedHost.hostId ? 0 : -1;
20335
+ tab.textContent = connectionHostLabel(host.hostId, host.active, messages);
20336
+ tab.addEventListener("click", () => {
20337
+ selectedHostId = host.hostId;
20338
+ render(latestSnapshot);
20339
+ });
20340
+ tabs.append(tab);
20341
+ }
20342
+ for (const agent of selectedHost.agents) {
20260
20343
  appendConnectionRow(
20261
20344
  document2,
20262
- content,
20345
+ hostSection,
20263
20346
  RENDERER_AGENT_LABELS[agent.agent],
20264
20347
  agent.availability,
20265
20348
  null,
20266
20349
  agent.error,
20267
20350
  messages,
20268
20351
  agent.agent,
20269
- agent
20352
+ agent,
20353
+ selectedHost.hostId
20270
20354
  );
20271
20355
  }
20356
+ content.append(tabs, hostSection);
20272
20357
  };
20273
20358
  const diagnostics = getDiagnostics();
20274
20359
  render(diagnostics?.snapshot() ?? null);
@@ -20278,7 +20363,11 @@ ${error51.stderrTail}`] : []
20278
20363
  }
20279
20364
  copyAll.addEventListener("click", () => {
20280
20365
  const snapshot = diagnostics.snapshot();
20281
- const report = snapshot.agents.map((agent) => diagnosticText(RENDERER_AGENT_LABELS[agent.agent], agent)).join("\n\n");
20366
+ const report = snapshot.hosts.flatMap(
20367
+ (host) => host.agents.map(
20368
+ (agent) => diagnosticText(host.hostId, RENDERER_AGENT_LABELS[agent.agent], agent)
20369
+ )
20370
+ ).join("\n\n");
20282
20371
  copyDiagnosticsToClipboard(document2, copyAll, report, messages, messages.connectionCopyAll);
20283
20372
  });
20284
20373
  const unsubscribe = diagnostics.subscribe(() => render(diagnostics.snapshot()));
@@ -20307,17 +20396,21 @@ ${error51.stderrTail}`] : []
20307
20396
  createRendererSettingsIcon("diagnose", 16),
20308
20397
  messages.connectionRefresh
20309
20398
  );
20399
+ const snapshot = diagnostics.snapshot();
20310
20400
  render({
20311
- ...diagnostics.snapshot(),
20312
- agents: diagnostics.snapshot().agents.map((agent) => ({
20313
- ...agent,
20314
- availability: "error",
20315
- error: {
20316
- code: "internalError",
20317
- message: error51 instanceof Error ? error51.message : String(error51),
20318
- retryable: true,
20319
- stage: "request"
20320
- }
20401
+ ...snapshot,
20402
+ hosts: snapshot.hosts.map((host) => ({
20403
+ ...host,
20404
+ agents: host.agents.map((agent) => ({
20405
+ ...agent,
20406
+ availability: "error",
20407
+ error: {
20408
+ code: "internalError",
20409
+ message: error51 instanceof Error ? error51.message : String(error51),
20410
+ retryable: true,
20411
+ stage: "request"
20412
+ }
20413
+ }))
20321
20414
  }))
20322
20415
  });
20323
20416
  }
@@ -20617,7 +20710,7 @@ ${error51.stderrTail}`] : []
20617
20710
  }
20618
20711
 
20619
20712
  // src/settings/shell.css
20620
- var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-header-actions {\n display: flex;\n align-items: center;\n flex: none;\n gap: 12px;\n margin-left: auto;\n}\n\n.settings-icon-button.settings-star-link {\n width: auto;\n gap: 6px;\n padding-inline: 8px;\n font-size: 12px;\n line-height: 18px;\n text-decoration: none;\n border: 2px dashed #f5c542;\n white-space: nowrap;\n}\n\n.settings-star-link .codexhost-settings-icon {\n color: #f5c542;\n fill: currentColor;\n stroke: currentColor;\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-page-description {\n max-width: 760px;\n margin: -12px 0 24px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-connection-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.settings-connection-list {\n min-width: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row {\n display: grid;\n grid-template-columns: minmax(180px, 1fr) auto minmax(260px, 1.5fr);\n align-items: center;\n min-width: 0;\n min-height: 76px;\n gap: 24px;\n padding: 14px 0;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n gap: 10px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 20px;\n}\n\n.settings-connection-row__identity strong {\n min-width: 0;\n overflow-wrap: anywhere;\n font-weight: 500;\n}\n\n.settings-connection-row__dot {\n width: 9px;\n height: 9px;\n flex: none;\n border-radius: 50%;\n background: var(--settings-muted);\n}\n\n.settings-connection-row__dot[data-connection-tone="ready"] {\n background: #22c55e;\n}\n\n.settings-connection-row__dot[data-connection-tone="checking"] {\n background: #f5c542;\n}\n\n.settings-connection-row__dot[data-connection-tone="failed"] {\n background: #ef4444;\n}\n\n.settings-status-badge[data-connection-tone="ready"] {\n color: #4ade80;\n}\n\n.settings-status-badge[data-connection-tone="checking"] {\n color: #f5c542;\n}\n\n.settings-status-badge[data-connection-tone="failed"] {\n color: #f87171;\n}\n\n.settings-connection-row__detail {\n display: grid;\n min-width: 0;\n align-items: center;\n gap: 2px 12px;\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-row__reason {\n min-width: 0;\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n.settings-connection-details-toggle {\n width: fit-content;\n padding: 0;\n color: var(--settings-focus);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n background: transparent;\n border: 0;\n cursor: pointer;\n}\n\n.settings-connection-details-toggle:hover {\n text-decoration: underline;\n}\n\n.settings-connection-details {\n display: grid;\n grid-column: 1 / -1;\n gap: 6px;\n min-width: 0;\n margin: 4px 0 2px 32px;\n padding: 12px;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 4%);\n border-left: 2px solid #ef4444;\n border-radius: 4px;\n}\n\n.settings-connection-detail-line {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n gap: 12px;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-detail-line span {\n color: var(--settings-muted);\n}\n\n.settings-connection-detail-line code {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n white-space: pre-wrap;\n}\n\n.settings-connection-stderr {\n max-height: 220px;\n min-width: 0;\n margin: 4px 0 0;\n padding: 10px;\n overflow: auto;\n color: #fca5a5;\n font:\n 12px/18px ui-monospace,\n "SFMono-Regular",\n Consolas,\n "Liberation Mono",\n monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(0 0 0 / 28%);\n border-radius: 4px;\n}\n\n.settings-connection-details .settings-command-button {\n justify-self: start;\n min-height: 30px;\n font-size: 12px;\n}\n\n.settings-connection-row__detail time {\n color: var(--settings-muted);\n opacity: 0.8;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n display: grid;\n min-width: 0;\n gap: 12px;\n color: var(--settings-muted);\n font-size: 14px;\n line-height: 22px;\n overflow-wrap: anywhere;\n}\n\n.settings-update-notes > :first-child {\n margin-top: 0;\n}\n\n.settings-update-notes > :last-child {\n margin-bottom: 0;\n}\n\n.settings-update-notes h1,\n.settings-update-notes h2,\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n margin: 6px 0 0;\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes h1 {\n font-size: 18px;\n line-height: 26px;\n}\n\n.settings-update-notes h2 {\n font-size: 17px;\n line-height: 24px;\n}\n\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n font-size: 14px;\n line-height: 22px;\n}\n\n.settings-update-notes p,\n.settings-update-notes ul,\n.settings-update-notes ol,\n.settings-update-notes pre,\n.settings-update-notes blockquote {\n margin: 0;\n}\n\n.settings-update-notes ul,\n.settings-update-notes ol {\n padding-left: 24px;\n}\n\n.settings-update-notes li {\n padding-left: 4px;\n}\n\n.settings-update-notes li::marker {\n color: var(--settings-muted);\n font-size: 0.85em;\n}\n\n.settings-update-notes li + li {\n margin-top: 14px;\n}\n\n.settings-update-notes .release-note-translation {\n display: block;\n margin-top: 3px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-notes blockquote {\n padding: 8px 12px;\n color: var(--settings-muted);\n border-left: 3px solid var(--settings-focus);\n background: rgb(255 255 255 / 4%);\n}\n\n.settings-update-notes hr {\n width: 100%;\n margin: 2px 0;\n border: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-update-notes strong {\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes a {\n color: #7cb8ff;\n text-decoration: none;\n}\n\n.settings-update-notes a:hover {\n text-decoration: underline;\n}\n\n.settings-update-notes code {\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n}\n\n.settings-update-notes :not(pre) > code {\n padding: 1px 5px;\n background: rgb(255 255 255 / 8%);\n border-radius: 4px;\n}\n\n.settings-update-notes pre {\n min-width: 0;\n max-width: 100%;\n padding: 10px 12px;\n overflow-x: auto;\n overflow-y: hidden;\n background: rgb(0 0 0 / 28%);\n border-radius: 6px;\n}\n\n.settings-update-notes pre code {\n line-height: 18px;\n white-space: pre-wrap;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-manual {\n display: grid;\n min-width: 0;\n gap: 6px;\n margin-top: 14px;\n padding-inline: 4px;\n}\n\n.settings-update-manual[hidden] {\n display: none;\n}\n\n.settings-update-manual span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-manual code {\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-icon-button.settings-star-link {\n width: 28px;\n padding: 0;\n }\n\n .settings-star-link span {\n display: none;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-connection-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 10px 16px;\n }\n\n .settings-connection-row__detail {\n grid-column: 1 / -1;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
20713
+ var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-header-actions {\n display: flex;\n align-items: center;\n flex: none;\n gap: 12px;\n margin-left: auto;\n}\n\n.settings-icon-button.settings-star-link {\n width: auto;\n gap: 6px;\n padding-inline: 8px;\n font-size: 12px;\n line-height: 18px;\n text-decoration: none;\n border: 2px dashed #f5c542;\n white-space: nowrap;\n}\n\n.settings-star-link .codexhost-settings-icon {\n color: #f5c542;\n fill: currentColor;\n stroke: currentColor;\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-page-description {\n max-width: 760px;\n margin: -12px 0 24px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-connection-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.settings-connection-list {\n min-width: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-connection-host-tabs {\n display: flex;\n gap: 6px;\n padding: 20px 0 8px;\n overflow-x: auto;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-host-tab {\n flex: none;\n padding: 7px 12px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--settings-muted);\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n}\n\n.settings-connection-host-tab:hover {\n background: var(--settings-hover);\n color: var(--settings-text);\n}\n\n.settings-connection-host-tab[aria-selected="true"] {\n background: var(--settings-active);\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-connection-host-tab:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: -2px;\n}\n\n.settings-connection-row {\n display: grid;\n grid-template-columns: minmax(180px, 1fr) auto minmax(260px, 1.5fr);\n align-items: center;\n min-width: 0;\n min-height: 76px;\n gap: 24px;\n padding: 14px 0;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n gap: 10px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 20px;\n}\n\n.settings-connection-row__identity strong {\n min-width: 0;\n overflow-wrap: anywhere;\n font-weight: 500;\n}\n\n.settings-connection-row__dot {\n width: 9px;\n height: 9px;\n flex: none;\n border-radius: 50%;\n background: var(--settings-muted);\n}\n\n.settings-connection-row__dot[data-connection-tone="ready"] {\n background: #22c55e;\n}\n\n.settings-connection-row__dot[data-connection-tone="checking"] {\n background: #f5c542;\n}\n\n.settings-connection-row__dot[data-connection-tone="failed"] {\n background: #ef4444;\n}\n\n.settings-status-badge[data-connection-tone="ready"] {\n color: #4ade80;\n}\n\n.settings-status-badge[data-connection-tone="checking"] {\n color: #f5c542;\n}\n\n.settings-status-badge[data-connection-tone="failed"] {\n color: #f87171;\n}\n\n.settings-connection-row__detail {\n display: grid;\n min-width: 0;\n align-items: center;\n gap: 2px 12px;\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-row__reason {\n min-width: 0;\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n.settings-connection-details-toggle {\n width: fit-content;\n padding: 0;\n color: var(--settings-focus);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n background: transparent;\n border: 0;\n cursor: pointer;\n}\n\n.settings-connection-details-toggle:hover {\n text-decoration: underline;\n}\n\n.settings-connection-details {\n display: grid;\n grid-column: 1 / -1;\n gap: 6px;\n min-width: 0;\n margin: 4px 0 2px 32px;\n padding: 12px;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 4%);\n border-left: 2px solid #ef4444;\n border-radius: 4px;\n}\n\n.settings-connection-detail-line {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n gap: 12px;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-detail-line span {\n color: var(--settings-muted);\n}\n\n.settings-connection-detail-line code {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n white-space: pre-wrap;\n}\n\n.settings-connection-stderr {\n max-height: 220px;\n min-width: 0;\n margin: 4px 0 0;\n padding: 10px;\n overflow: auto;\n color: #fca5a5;\n font:\n 12px/18px ui-monospace,\n "SFMono-Regular",\n Consolas,\n "Liberation Mono",\n monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(0 0 0 / 28%);\n border-radius: 4px;\n}\n\n.settings-connection-details .settings-command-button {\n justify-self: start;\n min-height: 30px;\n font-size: 12px;\n}\n\n.settings-connection-row__detail time {\n color: var(--settings-muted);\n opacity: 0.8;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n display: grid;\n min-width: 0;\n gap: 12px;\n color: var(--settings-muted);\n font-size: 14px;\n line-height: 22px;\n overflow-wrap: anywhere;\n}\n\n.settings-update-notes > :first-child {\n margin-top: 0;\n}\n\n.settings-update-notes > :last-child {\n margin-bottom: 0;\n}\n\n.settings-update-notes h1,\n.settings-update-notes h2,\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n margin: 6px 0 0;\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes h1 {\n font-size: 18px;\n line-height: 26px;\n}\n\n.settings-update-notes h2 {\n font-size: 17px;\n line-height: 24px;\n}\n\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n font-size: 14px;\n line-height: 22px;\n}\n\n.settings-update-notes p,\n.settings-update-notes ul,\n.settings-update-notes ol,\n.settings-update-notes pre,\n.settings-update-notes blockquote {\n margin: 0;\n}\n\n.settings-update-notes ul,\n.settings-update-notes ol {\n padding-left: 24px;\n}\n\n.settings-update-notes li {\n padding-left: 4px;\n}\n\n.settings-update-notes li::marker {\n color: var(--settings-muted);\n font-size: 0.85em;\n}\n\n.settings-update-notes li + li {\n margin-top: 14px;\n}\n\n.settings-update-notes .release-note-translation {\n display: block;\n margin-top: 3px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-notes blockquote {\n padding: 8px 12px;\n color: var(--settings-muted);\n border-left: 3px solid var(--settings-focus);\n background: rgb(255 255 255 / 4%);\n}\n\n.settings-update-notes hr {\n width: 100%;\n margin: 2px 0;\n border: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-update-notes strong {\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes a {\n color: #7cb8ff;\n text-decoration: none;\n}\n\n.settings-update-notes a:hover {\n text-decoration: underline;\n}\n\n.settings-update-notes code {\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n}\n\n.settings-update-notes :not(pre) > code {\n padding: 1px 5px;\n background: rgb(255 255 255 / 8%);\n border-radius: 4px;\n}\n\n.settings-update-notes pre {\n min-width: 0;\n max-width: 100%;\n padding: 10px 12px;\n overflow-x: auto;\n overflow-y: hidden;\n background: rgb(0 0 0 / 28%);\n border-radius: 6px;\n}\n\n.settings-update-notes pre code {\n line-height: 18px;\n white-space: pre-wrap;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-manual {\n display: grid;\n min-width: 0;\n gap: 6px;\n margin-top: 14px;\n padding-inline: 4px;\n}\n\n.settings-update-manual[hidden] {\n display: none;\n}\n\n.settings-update-manual span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-manual code {\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-icon-button.settings-star-link {\n width: 28px;\n padding: 0;\n }\n\n .settings-star-link span {\n display: none;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-connection-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 10px 16px;\n }\n\n .settings-connection-row__detail {\n grid-column: 1 / -1;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
20621
20714
 
20622
20715
  // src/settings/shell.ts
20623
20716
  var SETTINGS_SHELL_ATTRIBUTE = "data-codexhost-settings-shell";
@@ -21413,6 +21506,8 @@ ${error51.stderrTail}`] : []
21413
21506
  let modelControl = null;
21414
21507
  let usageNotificationDispose = null;
21415
21508
  const localAgentForSidebarThread = (input) => {
21509
+ const mountedHostId = modelControl?.currentHostId?.() ?? "local";
21510
+ if (input.hostId !== mountedHostId) return null;
21416
21511
  for (const mounted of mountedByComposer.values()) {
21417
21512
  const target = mounted.modelTarget;
21418
21513
  if (target?.[0] === "default" && input.draftId !== null && target[1] === input.draftId) {
@@ -21425,7 +21520,7 @@ ${error51.stderrTail}`] : []
21425
21520
  return null;
21426
21521
  };
21427
21522
  const sidebarAgentIcons = installRendererSidebarAgentIcons({
21428
- getClient: () => modelControl,
21523
+ getClient: (hostId) => modelClientForHost(hostId),
21429
21524
  getLocalAgent: localAgentForSidebarThread
21430
21525
  });
21431
21526
  let connectionDiagnostics = null;
@@ -21439,23 +21534,36 @@ ${error51.stderrTail}`] : []
21439
21534
  modelUpdates: 0,
21440
21535
  hook: null
21441
21536
  };
21442
- let harnessAvailability = Object.fromEntries(
21443
- externalAgents.map((agent) => [agent, "checking"])
21444
- );
21445
- const harnessAvailabilityErrors = {
21446
- pi: void 0,
21447
- "claude-code": void 0,
21448
- "deepseek-harness": void 0,
21449
- grok: void 0
21537
+ const createHostHarnessAvailabilityState = () => ({
21538
+ availability: Object.fromEntries(
21539
+ externalAgents.map((agent) => [agent, "checking"])
21540
+ ),
21541
+ errors: {
21542
+ pi: void 0,
21543
+ "claude-code": void 0,
21544
+ "deepseek-harness": void 0,
21545
+ grok: void 0
21546
+ },
21547
+ requestGeneration: 0,
21548
+ request: null,
21549
+ retryTimer: null,
21550
+ retryAttempt: 0
21551
+ });
21552
+ const harnessAvailabilityByHost = /* @__PURE__ */ new Map();
21553
+ const hostHarnessAvailabilityState = (hostId) => {
21554
+ let state = harnessAvailabilityByHost.get(hostId);
21555
+ if (!state) {
21556
+ state = createHostHarnessAvailabilityState();
21557
+ harnessAvailabilityByHost.set(hostId, state);
21558
+ }
21559
+ return state;
21450
21560
  };
21561
+ let activeAvailabilityHostId = "local";
21562
+ const activeHarnessAvailabilityState = () => hostHarnessAvailabilityState(activeAvailabilityHostId);
21451
21563
  const connectionListeners = /* @__PURE__ */ new Set();
21452
21564
  const publishConnectionStatus = () => {
21453
21565
  for (const listener of connectionListeners) listener();
21454
21566
  };
21455
- let availabilityRequestGeneration = 0;
21456
- let availabilityRequest = null;
21457
- let availabilityRetryTimer = null;
21458
- let availabilityRetryAttempt = 0;
21459
21567
  const availabilityRetryDelays = [500, 1e3, 2e3, 4e3, 8e3];
21460
21568
  const usageRefreshTimers = /* @__PURE__ */ new Map();
21461
21569
  const usageRefreshAttempts = /* @__PURE__ */ new Map();
@@ -21492,7 +21600,7 @@ ${error51.stderrTail}`] : []
21492
21600
  controller.get(mounted.composer),
21493
21601
  adapterStatus.state,
21494
21602
  controller.isSwitching(mounted.composer) || isOwnershipSubmissionBlocked(mounted.ownershipStatus),
21495
- harnessAvailability,
21603
+ activeHarnessAvailabilityState().availability,
21496
21604
  mounted.modelView,
21497
21605
  mounted.permissionModeView,
21498
21606
  mounted.usage,
@@ -21719,7 +21827,7 @@ ${error51.stderrTail}`] : []
21719
21827
  const state = controller.get(mounted.composer);
21720
21828
  if (state.agent === "codex") return;
21721
21829
  const agent = state.agent;
21722
- const availability = harnessAvailability[agent];
21830
+ const availability = activeHarnessAvailabilityState().availability[agent];
21723
21831
  if (availability !== "ready") {
21724
21832
  mounted.modelView = {
21725
21833
  status: adapterStatus.state !== "ready" || availability === "checking" ? "waitingForAdapter" : "error",
@@ -22232,7 +22340,9 @@ ${error51.stderrTail}`] : []
22232
22340
  }
22233
22341
  };
22234
22342
  const switchComposerAgent = async (mounted, agent) => {
22235
- if (agent !== "codex" && harnessAvailability[agent] !== "ready") return false;
22343
+ if (agent !== "codex" && activeHarnessAvailabilityState().availability[agent] !== "ready") {
22344
+ return false;
22345
+ }
22236
22346
  controller.clearPendingSubmission(mounted.composer);
22237
22347
  const composerId = controller.get(mounted.composer).composerId;
22238
22348
  controller.invalidateModelRequests(mounted.composer);
@@ -22279,53 +22389,67 @@ ${error51.stderrTail}`] : []
22279
22389
  const url2 = RENDERER_AGENT_INSTALL_URLS[agent];
22280
22390
  window.open(url2, "_blank", "noopener,noreferrer");
22281
22391
  };
22282
- const resetHarnessAvailabilityRetry = () => {
22283
- if (availabilityRetryTimer !== null) {
22284
- window.clearTimeout(availabilityRetryTimer);
22285
- availabilityRetryTimer = null;
22392
+ function resetHarnessAvailabilityRetry(hostId) {
22393
+ const state = hostHarnessAvailabilityState(hostId);
22394
+ if (state.retryTimer !== null) {
22395
+ window.clearTimeout(state.retryTimer);
22396
+ state.retryTimer = null;
22286
22397
  }
22287
- availabilityRetryAttempt = 0;
22288
- };
22289
- const scheduleHarnessAvailabilityRetry = () => {
22290
- if (disposed || availabilityRetryTimer !== null || availabilityRetryAttempt >= availabilityRetryDelays.length) {
22398
+ state.retryAttempt = 0;
22399
+ }
22400
+ function scheduleHarnessAvailabilityRetry(hostId) {
22401
+ const state = hostHarnessAvailabilityState(hostId);
22402
+ if (disposed || state.retryTimer !== null || state.retryAttempt >= availabilityRetryDelays.length) {
22291
22403
  return;
22292
22404
  }
22293
- const delay = availabilityRetryDelays[availabilityRetryAttempt];
22294
- availabilityRetryAttempt += 1;
22295
- availabilityRetryTimer = window.setTimeout(() => {
22296
- availabilityRetryTimer = null;
22297
- void refreshHarnessAvailability(true, true);
22405
+ const delay = availabilityRetryDelays[state.retryAttempt];
22406
+ state.retryAttempt += 1;
22407
+ state.retryTimer = window.setTimeout(() => {
22408
+ state.retryTimer = null;
22409
+ void refreshHarnessAvailabilityForHost(hostId, true, true);
22298
22410
  }, delay);
22299
- };
22300
- const refreshHarnessAvailability = (refresh = false, retry = false) => {
22301
- if (!retry) resetHarnessAvailabilityRetry();
22302
- if (!modelControl) return Promise.resolve();
22303
- const client = modelControl;
22304
- if (availabilityRequest?.client === client) return availabilityRequest.promise;
22305
- harnessAvailability = Object.fromEntries(
22411
+ }
22412
+ function modelClientForHost(hostId) {
22413
+ if (!modelControl) return null;
22414
+ const selected = modelControl.clientForHost?.(hostId);
22415
+ if (selected) return selected;
22416
+ const currentHostId = modelControl.currentHostId?.() ?? "local";
22417
+ return currentHostId === hostId ? modelControl : null;
22418
+ }
22419
+ function refreshHarnessAvailabilityForHost(hostId, refresh = false, retry = false) {
22420
+ const state = hostHarnessAvailabilityState(hostId);
22421
+ if (!retry) resetHarnessAvailabilityRetry(hostId);
22422
+ const client = modelClientForHost(hostId);
22423
+ if (!client) {
22424
+ scheduleHarnessAvailabilityRetry(hostId);
22425
+ return Promise.resolve();
22426
+ }
22427
+ if (state.request?.client === client) return state.request.promise;
22428
+ state.availability = Object.fromEntries(
22306
22429
  externalAgents.map((agent) => [
22307
22430
  agent,
22308
- harnessAvailability[agent] === "ready" ? "ready" : "checking"
22431
+ state.availability[agent] === "ready" ? "ready" : "checking"
22309
22432
  ])
22310
22433
  );
22311
- publishConnectionStatus();
22312
- for (const mounted of mountedByComposer.values()) renderMounted(mounted);
22313
- const generation = ++availabilityRequestGeneration;
22434
+ if (hostId === activeAvailabilityHostId) {
22435
+ publishConnectionStatus();
22436
+ for (const mounted of mountedByComposer.values()) renderMounted(mounted);
22437
+ }
22438
+ const generation = ++state.requestGeneration;
22314
22439
  const promise2 = (async () => {
22315
22440
  await Promise.all(
22316
22441
  externalAgents.map(async (agent) => {
22317
22442
  let status = "error";
22443
+ let nextError;
22318
22444
  try {
22319
22445
  const inspection = await client.inspectHarness({
22320
22446
  harnessId: externalHarnessIds[agent],
22321
22447
  refresh
22322
22448
  });
22323
22449
  status = inspection.status === "ready" ? "ready" : inspection.status;
22324
- if (inspection.status === "ready") {
22325
- harnessAvailabilityErrors[agent] = void 0;
22326
- } else {
22450
+ if (inspection.status !== "ready") {
22327
22451
  const error51 = inspection.error;
22328
- harnessAvailabilityErrors[agent] = {
22452
+ nextError = {
22329
22453
  code: error51.code,
22330
22454
  message: error51.message,
22331
22455
  retryable: error51.retryable,
@@ -22337,24 +22461,29 @@ ${error51.stderrTail}`] : []
22337
22461
  }
22338
22462
  } catch (error51) {
22339
22463
  status = "error";
22340
- harnessAvailabilityErrors[agent] = {
22464
+ nextError = {
22341
22465
  code: "internalError",
22342
22466
  message: error51 instanceof Error ? error51.message : String(error51),
22343
22467
  retryable: true,
22344
22468
  stage: "request"
22345
22469
  };
22346
22470
  }
22347
- if (generation !== availabilityRequestGeneration || disposed) return;
22348
- harnessAvailability = { ...harnessAvailability, [agent]: status };
22471
+ if (generation !== state.requestGeneration || disposed) return;
22472
+ state.errors[agent] = nextError;
22473
+ state.availability = { ...state.availability, [agent]: status };
22474
+ if (hostId !== activeAvailabilityHostId) {
22475
+ publishConnectionStatus();
22476
+ return;
22477
+ }
22349
22478
  for (const mounted of mountedByComposer.values()) {
22350
- const state = controller.get(mounted.composer);
22351
- if (adapterStatus.state === "ready" && state.phase === "draft" && state.agent === agent && status !== "ready") {
22479
+ const composerState = controller.get(mounted.composer);
22480
+ if (adapterStatus.state === "ready" && composerState.phase === "draft" && composerState.agent === agent && status !== "ready") {
22352
22481
  await switchComposerAgent(mounted, "codex");
22353
22482
  }
22354
22483
  }
22355
22484
  for (const mounted of mountedByComposer.values()) {
22356
- const state = controller.get(mounted.composer);
22357
- if (state.agent === agent) {
22485
+ const composerState = controller.get(mounted.composer);
22486
+ if (composerState.agent === agent) {
22358
22487
  void loadExternalCatalog(mounted);
22359
22488
  }
22360
22489
  renderMounted(mounted);
@@ -22362,37 +22491,66 @@ ${error51.stderrTail}`] : []
22362
22491
  publishConnectionStatus();
22363
22492
  })
22364
22493
  );
22365
- if (externalAgents.every((agent) => harnessAvailability[agent] === "ready")) {
22366
- resetHarnessAvailabilityRetry();
22494
+ if (generation !== state.requestGeneration || disposed) return;
22495
+ if (externalAgents.every((agent) => state.availability[agent] === "ready")) {
22496
+ resetHarnessAvailabilityRetry(hostId);
22367
22497
  } else {
22368
- scheduleHarnessAvailabilityRetry();
22498
+ scheduleHarnessAvailabilityRetry(hostId);
22369
22499
  }
22370
22500
  })();
22371
22501
  const request = { client, promise: promise2 };
22372
- availabilityRequest = request;
22502
+ state.request = request;
22373
22503
  void promise2.then(
22374
22504
  () => {
22375
- if (availabilityRequest === request) availabilityRequest = null;
22505
+ if (state.request === request) state.request = null;
22376
22506
  },
22377
22507
  () => {
22378
- if (availabilityRequest === request) availabilityRequest = null;
22508
+ if (state.request === request) state.request = null;
22379
22509
  }
22380
22510
  );
22381
22511
  return promise2;
22512
+ }
22513
+ function reconcileHarnessAvailabilityHost() {
22514
+ const reportedHostId = modelControl?.currentHostId?.();
22515
+ const hostId = reportedHostId ?? (modelControl?.currentHostId ? activeAvailabilityHostId : "local");
22516
+ if (hostId === activeAvailabilityHostId) return;
22517
+ activeAvailabilityHostId = hostId;
22518
+ hostHarnessAvailabilityState(hostId);
22519
+ publishConnectionStatus();
22520
+ for (const mounted of mountedByComposer.values()) renderMounted(mounted);
22521
+ void refreshHarnessAvailabilityForHost(hostId);
22522
+ }
22523
+ const refreshHarnessAvailability = (refresh = false) => {
22524
+ reconcileHarnessAvailabilityHost();
22525
+ return refreshHarnessAvailabilityForHost(activeAvailabilityHostId, refresh);
22382
22526
  };
22383
22527
  connectionDiagnostics = {
22384
22528
  snapshot() {
22529
+ const hostIds = [
22530
+ "local",
22531
+ ...[...harnessAvailabilityByHost.keys()].filter((hostId) => hostId !== "local").sort()
22532
+ ];
22385
22533
  return {
22386
22534
  adapter: { ...adapterStatus },
22387
- agents: externalAgents.map((agent) => ({
22388
- agent,
22389
- availability: harnessAvailability[agent] ?? "checking",
22390
- error: harnessAvailabilityErrors[agent] ?? null
22391
- }))
22535
+ hosts: hostIds.map((hostId) => {
22536
+ const state = hostHarnessAvailabilityState(hostId);
22537
+ return {
22538
+ hostId,
22539
+ active: hostId === activeAvailabilityHostId,
22540
+ agents: externalAgents.map((agent) => ({
22541
+ agent,
22542
+ availability: state.availability[agent] ?? "checking",
22543
+ error: state.errors[agent] ?? null
22544
+ }))
22545
+ };
22546
+ })
22392
22547
  };
22393
22548
  },
22394
22549
  refresh() {
22395
- return refreshHarnessAvailability(true);
22550
+ for (const hostId of harnessAvailabilityByHost.keys()) {
22551
+ void refreshHarnessAvailabilityForHost(hostId, true);
22552
+ }
22553
+ return Promise.resolve();
22396
22554
  },
22397
22555
  subscribe(listener) {
22398
22556
  connectionListeners.add(listener);
@@ -22523,6 +22681,16 @@ ${error51.stderrTail}`] : []
22523
22681
  const composer = composerForEditor(editor);
22524
22682
  if (composer) mount(composer);
22525
22683
  }
22684
+ const localAvailability = hostHarnessAvailabilityState("local").availability;
22685
+ if (externalAgents.some((agent) => localAvailability[agent] === "checking")) {
22686
+ void refreshHarnessAvailabilityForHost("local");
22687
+ }
22688
+ reconcileHarnessAvailabilityHost();
22689
+ if (externalAgents.some(
22690
+ (agent) => activeHarnessAvailabilityState().availability[agent] === "checking"
22691
+ )) {
22692
+ void refreshHarnessAvailability();
22693
+ }
22526
22694
  pendingReplacements.clear();
22527
22695
  };
22528
22696
  const scheduleScan = (refreshTargets = false) => {
@@ -22677,10 +22845,15 @@ ${error51.stderrTail}`] : []
22677
22845
  transferReplacedComposers(mutations);
22678
22846
  scheduleScan(mutations.some(mutationMayChangeComposerTarget));
22679
22847
  });
22848
+ const onHostRouteChange = () => {
22849
+ reconcileHarnessAvailabilityHost();
22850
+ void refreshHarnessAvailability();
22851
+ };
22680
22852
  const onAdapterStatus = () => {
22681
22853
  publishConnectionStatus();
22682
22854
  if (adapterStatus.state === "ready") {
22683
22855
  sidebarAgentIcons.refresh();
22856
+ void refreshHarnessAvailabilityForHost("local");
22684
22857
  void refreshHarnessAvailability();
22685
22858
  for (const mounted of mountedByComposer.values()) {
22686
22859
  if (mounted.modelView.status === "waitingForAdapter" && mounted.composer.isConnected && applyComposerAgent(mounted.composer)) {
@@ -22702,10 +22875,17 @@ ${error51.stderrTail}`] : []
22702
22875
  document.addEventListener("keydown", onKeyDown, true);
22703
22876
  document.addEventListener("click", onClick, true);
22704
22877
  const onWindowFocus = () => {
22705
- if (externalAgents.some((agent) => harnessAvailability[agent] !== "ready")) {
22878
+ reconcileHarnessAvailabilityHost();
22879
+ const local = hostHarnessAvailabilityState("local");
22880
+ if (externalAgents.some((agent) => local.availability[agent] !== "ready")) {
22881
+ void refreshHarnessAvailabilityForHost("local", true);
22882
+ }
22883
+ const active = activeHarnessAvailabilityState();
22884
+ if (externalAgents.some((agent) => active.availability[agent] !== "ready")) {
22706
22885
  void refreshHarnessAvailability(true);
22707
22886
  }
22708
22887
  };
22888
+ window.addEventListener("codexhost:draft-prewarm-policy-changed", onHostRouteChange);
22709
22889
  window.addEventListener("codexhost:renderer-adapter-status", onAdapterStatus);
22710
22890
  window.addEventListener("focus", onWindowFocus);
22711
22891
  const connectedComposers = () => [...mountedByComposer.values()].filter(
@@ -22722,7 +22902,7 @@ ${error51.stderrTail}`] : []
22722
22902
  version: 2,
22723
22903
  mountedComposers: selections.length,
22724
22904
  enabledAgents: [...enabledAgents],
22725
- availability: { ...harnessAvailability },
22905
+ availability: { ...activeHarnessAvailabilityState().availability },
22726
22906
  selections,
22727
22907
  adapter: { ...adapterStatus }
22728
22908
  };
@@ -22769,8 +22949,16 @@ ${error51.stderrTail}`] : []
22769
22949
  } catch {
22770
22950
  }
22771
22951
  });
22952
+ for (const state of harnessAvailabilityByHost.values()) {
22953
+ state.requestGeneration += 1;
22954
+ state.request = null;
22955
+ if (state.retryTimer !== null) window.clearTimeout(state.retryTimer);
22956
+ }
22957
+ harnessAvailabilityByHost.clear();
22958
+ activeAvailabilityHostId = "local";
22772
22959
  sidebarAgentIcons.refresh();
22773
- void refreshHarnessAvailability();
22960
+ void refreshHarnessAvailabilityForHost("local");
22961
+ reconcileHarnessAvailabilityHost();
22774
22962
  const connected = connectedComposers();
22775
22963
  if (connected.length === 1) {
22776
22964
  const mounted = connected[0];
@@ -22803,9 +22991,14 @@ ${error51.stderrTail}`] : []
22803
22991
  document.removeEventListener("submit", onSubmit, true);
22804
22992
  document.removeEventListener("keydown", onKeyDown, true);
22805
22993
  document.removeEventListener("click", onClick, true);
22994
+ window.removeEventListener("codexhost:draft-prewarm-policy-changed", onHostRouteChange);
22806
22995
  window.removeEventListener("codexhost:renderer-adapter-status", onAdapterStatus);
22807
22996
  window.removeEventListener("focus", onWindowFocus);
22808
- resetHarnessAvailabilityRetry();
22997
+ for (const state of harnessAvailabilityByHost.values()) {
22998
+ state.requestGeneration += 1;
22999
+ if (state.retryTimer !== null) window.clearTimeout(state.retryTimer);
23000
+ }
23001
+ harnessAvailabilityByHost.clear();
22809
23002
  for (const timer of usageRefreshTimers.values()) window.clearTimeout(timer);
22810
23003
  usageRefreshTimers.clear();
22811
23004
  for (const mounted of mountedByComposer.values()) {