@karlorz/codexhost-cli-win32-x64 0.13.3-7 → 0.14.0-0

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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/app/codexhost-distribution.json +1 -1
  3. package/app/console-server.mjs +55 -4
  4. package/app/console-web.js +344 -54
  5. package/app/cpa-live-voice-hook.js +55 -4
  6. package/app/desktop-controller.mjs +69 -8
  7. package/app/host-runtime.mjs +743 -230
  8. package/app/plugins/antigravity/plugin.mjs +88 -12
  9. package/app/plugins/claude-code/plugin.mjs +397 -21
  10. package/app/plugins/codebuddy/plugin.mjs +107 -17
  11. package/app/plugins/codex-usage/plugin.mjs +67 -7
  12. package/app/plugins/copilot/assets/icon.png +0 -0
  13. package/app/plugins/copilot/manifest.json +26 -0
  14. package/app/plugins/copilot/plugin.mjs +34645 -0
  15. package/app/plugins/cursor-cli/plugin.mjs +88 -6
  16. package/app/plugins/deepseek-harness/plugin.mjs +73 -6
  17. package/app/plugins/enabled.json +1 -0
  18. package/app/plugins/grok/plugin.mjs +272 -60
  19. package/app/plugins/hermes/plugin.mjs +70 -4
  20. package/app/plugins/kimi-code/plugin.mjs +73 -6
  21. package/app/plugins/kiro-cli/plugin.mjs +73 -6
  22. package/app/plugins/omp/plugin.mjs +73 -112
  23. package/app/plugins/opencode/plugin.mjs +75 -6
  24. package/app/plugins/pi/plugin.mjs +204 -44
  25. package/app/plugins/qoder/plugin.mjs +73 -6
  26. package/app/plugins/qoder-cn/plugin.mjs +73 -6
  27. package/app/plugins/workbuddy/plugin.mjs +368 -41
  28. package/app/plugins/zcode/plugin.mjs +74 -6
  29. package/app/renderer-extension.js +749 -157
  30. package/bin/codexhost.exe +0 -0
  31. package/libexec/codexhost-node-repl.exe +0 -0
  32. package/libexec/codexhost-shim.exe +0 -0
  33. package/libexec/codexhost-updater.exe +0 -0
  34. package/package.json +1 -1
@@ -14700,10 +14700,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14700
14700
  var project = external_exports.string().min(1).max(4096);
14701
14701
  var usageStatisticsParamsSchema = external_exports.strictObject({
14702
14702
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
14703
+ /** Display timezone for ranges, dates and hourly buckets; omitted means the Host's local zone. */
14704
+ timeZone: external_exports.string().min(1).max(128).refine((value) => {
14705
+ try {
14706
+ new Intl.DateTimeFormat("en", { timeZone: value });
14707
+ return true;
14708
+ } catch {
14709
+ return false;
14710
+ }
14711
+ }, "Unsupported usage statistics timezone").optional(),
14703
14712
  harness: harness.optional(),
14704
14713
  model: model.nullable().optional(),
14705
14714
  project: project.nullable().optional(),
14706
- /** One local date inside the range. */
14715
+ /** One date in the query timezone inside the range. */
14707
14716
  date: localDate.optional()
14708
14717
  });
14709
14718
  var usageStatisticsTotalsSchema = external_exports.strictObject({
@@ -14739,7 +14748,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14739
14748
  });
14740
14749
  var usageStatisticsResultSchema = external_exports.strictObject({
14741
14750
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
14742
- /** First local date of the range; null for all time. */
14751
+ /** First date in the query timezone (Host local when omitted); null for all time. */
14743
14752
  from: localDate.nullable(),
14744
14753
  to: localDate,
14745
14754
  /** The filters these figures apply, echoed so a late answer to older filters is recognised. */
@@ -14779,7 +14788,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14779
14788
  totals: usageStatisticsTotalsSchema,
14780
14789
  /** Each date and Harness of the range: every filter except the day. */
14781
14790
  daily: external_exports.array(external_exports.strictObject({ date: localDate, harness, ...totals })).max(2e5),
14782
- /** Local weekday (0 = Sunday) and hour, all filters applied. */
14791
+ /** Weekday (0 = Sunday) and hour in the query timezone, all filters applied. */
14783
14792
  hourly: external_exports.array(external_exports.strictObject({
14784
14793
  weekday: external_exports.number().int().min(0).max(6),
14785
14794
  hour: external_exports.number().int().min(0).max(23),
@@ -14890,6 +14899,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14890
14899
  "codexhost/harness/display-settings/set",
14891
14900
  "codexhost/harness/inspect",
14892
14901
  "codexhost/harness/installation",
14902
+ "codexhost/harness/installation/start",
14903
+ "codexhost/harness/installation/status",
14893
14904
  "codexhost/harness/launch-settings/get",
14894
14905
  "codexhost/harness/launch-settings/set",
14895
14906
  "codexhost/harness/grok-computer-use/get",
@@ -14970,7 +14981,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14970
14981
  var nonNegativeSafeIntegerSchema = external_exports.number().int().safe().nonnegative();
14971
14982
  var finiteNonNegativeNumberSchema = external_exports.number().finite().nonnegative();
14972
14983
  var cacheHitRatePercentSchema = external_exports.number().finite().min(0).max(100);
14984
+ var indicatorTextSchema = (maximum) => harnessLocalizedTextSchema.refine((entries) => Object.values(entries).every((text) => text.length <= maximum));
14985
+ var composerIndicatorsSchema = external_exports.array(external_exports.object({
14986
+ kind: external_exports.literal("countdown"),
14987
+ id: external_exports.string().min(1).max(64),
14988
+ expiresAtUnix: nonNegativeSafeIntegerSchema,
14989
+ label: indicatorTextSchema(256).optional(),
14990
+ tooltip: indicatorTextSchema(1024).optional()
14991
+ }).strict()).max(2).refine((entries) => new Set(entries.map(({ id }) => id)).size === entries.length, {
14992
+ message: "Composer indicator IDs must be unique"
14993
+ });
14973
14994
  var threadUsageSnapshotSchema = external_exports.object({
14995
+ composerIndicators: composerIndicatorsSchema.optional(),
14974
14996
  inputTokens: nonNegativeSafeIntegerSchema.optional(),
14975
14997
  cachedInputTokens: nonNegativeSafeIntegerSchema.optional(),
14976
14998
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
@@ -15386,7 +15408,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15386
15408
  }).strict().optional(),
15387
15409
  autonomousTurns: external_exports.object({
15388
15410
  observe: external_exports.boolean()
15389
- }).strict().optional()
15411
+ }).strict().optional(),
15412
+ /** True only when `turn.steer` delivers input into the active Turn through a native primitive. */
15413
+ steer: external_exports.boolean().optional()
15390
15414
  }).strict();
15391
15415
  var harnessConfigurationStateSchema = external_exports.object({
15392
15416
  effectiveModel: harnessModelRefSchema.optional(),
@@ -15519,6 +15543,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15519
15543
  seen.add(thread.threadId);
15520
15544
  }
15521
15545
  });
15546
+ var threadSteeringInspectParamsSchema = external_exports.object({
15547
+ threadId: hostThreadIdSchema,
15548
+ /** The text about to be steered; a Harness command always stops and restarts. */
15549
+ text: external_exports.string().optional()
15550
+ }).strict();
15551
+ var THREAD_STEER_DELIVERY_PARAM = "codexhostSteerDelivery";
15552
+ var threadSteerDeliverySchema = external_exports.enum(["activeTurn", "newTurn"]);
15553
+ var threadSteeringInspectResultSchema = external_exports.object({
15554
+ delivery: external_exports.enum(["official", "activeTurn", "newTurn"])
15555
+ }).strict();
15522
15556
 
15523
15557
  // ../shared-contracts/dist/harness-plugins.js
15524
15558
  var HARNESS_PLUGIN_MANIFEST_MAX_BYTES = 32 * 1024;
@@ -15659,7 +15693,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15659
15693
  }).strict();
15660
15694
 
15661
15695
  // ../shared-contracts/dist/harness-installation.js
15662
- var HARNESS_INSTALLATION_METHOD = "codexhost/harness/installation";
15696
+ var HARNESS_INSTALLATION_START_METHOD = "codexhost/harness/installation/start";
15697
+ var HARNESS_INSTALLATION_STATUS_METHOD = "codexhost/harness/installation/status";
15663
15698
  var harnessInstallationParamsSchema = external_exports.object({
15664
15699
  harnessId: harnessIdSchema,
15665
15700
  action: external_exports.enum(["check", "update", "install"])
@@ -15669,6 +15704,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15669
15704
  latestVersion: external_exports.string().min(1).max(128),
15670
15705
  updateAvailable: external_exports.boolean(),
15671
15706
  canUpdate: external_exports.boolean(),
15707
+ /** Defaults to CLI; some Harnesses ship only inside a desktop application. */
15708
+ versionKind: external_exports.enum(["cli", "application"]).optional(),
15709
+ /** Official download opened by the user; does not imply an automatic installation. */
15710
+ manualUpdateUrl: external_exports.url().max(2048).refine((value) => /^https:\/\/[^/?#@]+(?:[/?#]|$)/iu.test(value), "Manual updates require a credential-free HTTPS URL").optional(),
15672
15711
  // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
15673
15712
  message: external_exports.string().max(2048).optional(),
15674
15713
  messageCode: external_exports.string().min(1).max(128).optional(),
@@ -15676,6 +15715,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15676
15715
  notice: harnessLocalizedTextSchema.optional(),
15677
15716
  latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
15678
15717
  }).strict();
15718
+ var harnessInstallationTaskParamsSchema = external_exports.object({ taskId: external_exports.string().uuid() }).strict();
15719
+ var harnessInstallationTaskSchema = external_exports.discriminatedUnion("status", [
15720
+ external_exports.object({ taskId: external_exports.string().uuid(), status: external_exports.literal("running") }).strict(),
15721
+ external_exports.object({
15722
+ taskId: external_exports.string().uuid(),
15723
+ status: external_exports.literal("succeeded"),
15724
+ result: harnessInstallationStateSchema
15725
+ }).strict(),
15726
+ external_exports.object({
15727
+ taskId: external_exports.string().uuid(),
15728
+ status: external_exports.literal("failed"),
15729
+ error: external_exports.object({ code: external_exports.number().int(), message: external_exports.string().min(1).max(2048) }).strict()
15730
+ }).strict()
15731
+ ]);
15679
15732
 
15680
15733
  // ../shared-contracts/dist/codex-accounts.js
15681
15734
  var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
@@ -17138,9 +17191,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17138
17191
  clearPendingSubmission(composer) {
17139
17192
  this.#pendingSubmissions.delete(this.#state(composer));
17140
17193
  }
17194
+ /** Only a draft's submission creates a Thread, so only it sets the next new-Thread Agent. */
17141
17195
  recordSubmission(composer) {
17142
17196
  const state = this.#state(composer);
17143
- this.#lastSubmittedAgent = state.agent;
17197
+ if (state.phase === "draft") this.#lastSubmittedAgent = state.agent;
17144
17198
  return state;
17145
17199
  }
17146
17200
  transfer(source, replacement, target = null) {
@@ -17649,9 +17703,63 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17649
17703
  };
17650
17704
  }
17651
17705
 
17652
- // src/codex-ssh-adapter.ts
17706
+ // src/codex-native-services.ts
17707
+ async function loadCodexNativeServices(ownerWindow, matchesServices, importModule = (url2) => import(url2)) {
17708
+ if (!ownerWindow.document || !ownerWindow.location) return null;
17709
+ const page = new URL(ownerWindow.location.href);
17710
+ const urls = /* @__PURE__ */ new Set();
17711
+ for (const link of ownerWindow.document.querySelectorAll('link[rel="modulepreload"][href]')) {
17712
+ const url2 = new URL(link.getAttribute("href") ?? "", page);
17713
+ if (url2.protocol === page.protocol && url2.host === page.host && url2.pathname.endsWith(".js") && !url2.search && !url2.hash)
17714
+ urls.add(url2.href);
17715
+ }
17716
+ const candidates = [...urls].sort(
17717
+ (a, b) => Number(b.includes("/app-shared-")) - Number(a.includes("/app-shared-"))
17718
+ );
17719
+ for (const url2 of candidates) {
17720
+ let module;
17721
+ try {
17722
+ module = await importModule(url2);
17723
+ } catch {
17724
+ continue;
17725
+ }
17726
+ const matches = new Set(Object.values(module).filter(matchesServices));
17727
+ if (matches.size > 1) throw new Error("Codex native services are unavailable in this window");
17728
+ if (matches.size === 1) return [...matches][0] ?? null;
17729
+ }
17730
+ return null;
17731
+ }
17732
+
17733
+ // src/codex-ssh-services.ts
17653
17734
  var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17735
+ function isSshServices(value) {
17736
+ if (!record3(value) || !Object.hasOwn(value, "sshConnections") || !Object.hasOwn(value, "remoteConnections"))
17737
+ return false;
17738
+ const ssh = value.sshConnections;
17739
+ const remote = value.remoteConnections;
17740
+ return ssh != null && remote != null && typeof ssh.refresh === "function" && typeof ssh.save === "function" && typeof remote.getState === "function" && typeof remote.setAutoConnect === "function";
17741
+ }
17742
+ async function loadCodexSshServices(ownerWindow, importModule) {
17743
+ return loadCodexNativeServices(ownerWindow, isSshServices, importModule);
17744
+ }
17745
+
17746
+ // src/codex-ssh-adapter.ts
17747
+ var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17654
17748
  function createCodexSshClient(ownerWindow) {
17749
+ let nativeServices;
17750
+ function services() {
17751
+ nativeServices ??= loadCodexSshServices(ownerWindow).then(
17752
+ (value) => {
17753
+ if (!value) nativeServices = void 0;
17754
+ return value;
17755
+ },
17756
+ (error51) => {
17757
+ nativeServices = void 0;
17758
+ throw error51;
17759
+ }
17760
+ );
17761
+ return nativeServices;
17762
+ }
17655
17763
  function request(operation, params, signal) {
17656
17764
  const bridge = ownerWindow.electronBridge;
17657
17765
  if (!bridge?.sendMessageFromView)
@@ -17659,7 +17767,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17659
17767
  if (signal?.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
17660
17768
  return new Promise((resolve, reject) => {
17661
17769
  const requestId2 = ownerWindow.crypto.randomUUID();
17770
+ let settled = false;
17662
17771
  const finish = (error51, value) => {
17772
+ if (settled) return;
17773
+ settled = true;
17663
17774
  ownerWindow.clearTimeout(timer);
17664
17775
  ownerWindow.removeEventListener("message", receive);
17665
17776
  signal?.removeEventListener("abort", abort);
@@ -17669,10 +17780,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17669
17780
  const abort = () => finish(new DOMException("Aborted", "AbortError"));
17670
17781
  const receive = (event) => {
17671
17782
  const message2 = event.data;
17672
- if (!record3(message2) || message2.type !== "fetch-response" || message2.requestId !== requestId2)
17783
+ if (!record4(message2) || message2.type !== "fetch-response" || message2.requestId !== requestId2)
17673
17784
  return;
17674
17785
  if (message2.responseType !== "success" || typeof message2.status !== "number" || message2.status < 200 || message2.status >= 300 || typeof message2.bodyJsonString !== "string") {
17675
- finish(new Error("Codex SSH request failed; check the native Connections settings"));
17786
+ const detail = typeof message2.error === "string" ? `: ${message2.error}` : "";
17787
+ finish(
17788
+ new Error(`Codex SSH request failed; check the native Connections settings${detail}`)
17789
+ );
17676
17790
  return;
17677
17791
  }
17678
17792
  try {
@@ -17687,30 +17801,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17687
17801
  );
17688
17802
  ownerWindow.addEventListener("message", receive);
17689
17803
  signal?.addEventListener("abort", abort, { once: true });
17690
- try {
17691
- Promise.resolve(
17692
- bridge.sendMessageFromView({
17804
+ void (async () => {
17805
+ const native = await services();
17806
+ if (settled) return;
17807
+ if (native) {
17808
+ let result;
17809
+ switch (operation) {
17810
+ case "refresh-remote-connections":
17811
+ result = await native.sshConnections.refresh();
17812
+ break;
17813
+ case "save-codex-managed-remote-ssh-connections":
17814
+ result = await native.sshConnections.save(params);
17815
+ break;
17816
+ case "set-remote-connection-auto-connect":
17817
+ result = await native.remoteConnections.setAutoConnect(params);
17818
+ break;
17819
+ case "app-server-connection-state":
17820
+ result = await native.remoteConnections.getState(String(params.hostId));
17821
+ break;
17822
+ }
17823
+ finish(null, result);
17824
+ } else {
17825
+ await bridge.sendMessageFromView({
17693
17826
  type: "fetch",
17694
17827
  requestId: requestId2,
17695
17828
  method: "POST",
17696
17829
  url: `vscode://codex/${operation}`,
17697
17830
  body: JSON.stringify(params),
17698
17831
  reportUploadProgress: false
17699
- })
17700
- ).catch((error51) => finish(error51));
17701
- } catch (error51) {
17702
- finish(error51);
17703
- }
17832
+ });
17833
+ }
17834
+ })().catch((error51) => finish(error51));
17704
17835
  });
17705
17836
  }
17706
17837
  async function list(signal) {
17707
17838
  const result = await request("refresh-remote-connections", {}, signal);
17708
- if (!record3(result) || !Array.isArray(result.remoteConnections))
17839
+ if (!record4(result) || !Array.isArray(result.remoteConnections))
17709
17840
  throw new Error("Invalid Codex SSH connection list");
17710
17841
  return result.remoteConnections.filter(
17711
- (item) => !(record3(item) && ["wsl", "remote-control"].includes(String(item.source)))
17842
+ (item) => !(record4(item) && ["wsl", "remote-control"].includes(String(item.source)))
17712
17843
  ).map((item) => {
17713
- if (!record3(item) || typeof item.hostId !== "string" || typeof item.displayName !== "string" || !["codex-managed", "discovered"].includes(String(item.source)) || typeof item.sshHost !== "string" || typeof item.autoConnect !== "boolean" || !(item.sshAlias == null || typeof item.sshAlias === "string") || !(item.identity == null || typeof item.identity === "string") || !(item.sshPort == null || Number.isInteger(item.sshPort) && Number(item.sshPort) > 0 && Number(item.sshPort) <= 65535)) {
17844
+ if (!record4(item) || typeof item.hostId !== "string" || typeof item.displayName !== "string" || !["codex-managed", "discovered"].includes(String(item.source)) || typeof item.sshHost !== "string" || typeof item.autoConnect !== "boolean" || !(item.sshAlias == null || typeof item.sshAlias === "string") || !(item.identity == null || typeof item.identity === "string") || !(item.sshPort == null || Number.isInteger(item.sshPort) && Number(item.sshPort) > 0 && Number(item.sshPort) <= 65535)) {
17714
17845
  throw new Error(
17715
17846
  "This Codex version uses an unsupported SSH configuration; open native settings"
17716
17847
  );
@@ -17782,11 +17913,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17782
17913
  },
17783
17914
  async state(hostId2, signal) {
17784
17915
  const result = await request("app-server-connection-state", { hostId: hostId2 }, signal);
17785
- if (!record3(result) || typeof result.state !== "string")
17916
+ if (!record4(result) || typeof result.state !== "string")
17786
17917
  throw new Error("Invalid Codex connection state");
17787
17918
  return {
17788
17919
  state: result.state,
17789
- error: typeof result.error === "string" ? result.error : record3(result.error) && typeof result.error.message === "string" ? result.error.message : null
17920
+ error: typeof result.error === "string" ? result.error : record4(result.error) && typeof result.error.message === "string" ? result.error.message : null
17790
17921
  };
17791
17922
  }
17792
17923
  };
@@ -19145,17 +19276,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19145
19276
  let busy = false;
19146
19277
  let updating = false;
19147
19278
  let unsupported = false;
19279
+ let updatePromise = null;
19148
19280
  const render = () => {
19281
+ heading.textContent = state?.versionKind === "application" ? messages.harnessVersionApplicationTitle : messages.harnessVersionTitle;
19149
19282
  current.textContent = `${messages.harnessVersionCurrent}: ${state?.currentVersion ?? "\u2014"}`;
19150
19283
  latest.textContent = `${messages.harnessVersionLatest}: ${harnessVersionLabel(state, messages.harnessVersion)}`;
19151
19284
  update.disabled = busy || signal.aborted || !state?.canUpdate || !state.updateAvailable;
19285
+ const manualUrl = !state?.canUpdate && state?.updateAvailable ? state.manualUpdateUrl : void 0;
19286
+ actions.replaceChildren(update);
19287
+ update.hidden = !busy && !signal.aborted && Boolean(manualUrl);
19288
+ if (update.hidden && manualUrl) {
19289
+ const download2 = document2.createElement("a");
19290
+ download2.className = "settings-command-button settings-command-button--secondary";
19291
+ download2.dataset.harnessVersionAction = "download-update";
19292
+ download2.textContent = messages.harnessVersionDownloadUpdate;
19293
+ download2.href = manualUrl;
19294
+ download2.target = "_blank";
19295
+ download2.rel = "noopener noreferrer";
19296
+ actions.append(download2);
19297
+ }
19152
19298
  update.textContent = state?.canUpdate && state.latestVersion !== "Unknown" && !state.updateAvailable ? messages.harnessVersionUpToDate : messages.harnessVersionUpdate;
19153
19299
  const detail = harnessVersionNote(state, messages.harnessVersion);
19154
- note.textContent = detail ? `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
19300
+ note.textContent = detail ? !state?.canUpdate ? detail : `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
19155
19301
  };
19156
19302
  const run = async (action) => {
19157
- if (busy || unsupported || signal.aborted) return;
19158
- if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return;
19303
+ if (busy || unsupported || signal.aborted) return false;
19304
+ if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return false;
19159
19305
  busy = true;
19160
19306
  updating = action === "update";
19161
19307
  onChange();
@@ -19163,14 +19309,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19163
19309
  render();
19164
19310
  try {
19165
19311
  const result = await client.run(action);
19166
- if (signal.aborted) return;
19312
+ if (signal.aborted) return false;
19167
19313
  state = result;
19168
19314
  status.textContent = !result.canUpdate ? messages.harnessVersionManual : action === "update" ? result.updateAvailable ? messages.harnessVersionFailed : messages.harnessVersionUpdated : "";
19315
+ return result.canUpdate && !result.updateAvailable;
19169
19316
  } catch (error51) {
19170
- if (signal.aborted) return;
19317
+ if (signal.aborted) return false;
19171
19318
  unsupported = typeof error51 === "object" && error51 !== null && "code" in error51 && (error51.code === -32601 || error51.code === -32078);
19172
19319
  status.textContent = unsupported ? messages.harnessVersionUnsupported : messages.harnessVersionFailed;
19173
19320
  if (state) state = { ...state, canUpdate: false };
19321
+ return false;
19174
19322
  } finally {
19175
19323
  busy = false;
19176
19324
  updating = false;
@@ -19180,7 +19328,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19180
19328
  }
19181
19329
  }
19182
19330
  };
19183
- update.addEventListener("click", () => void run("update"));
19331
+ const updateVersion = () => {
19332
+ updatePromise ??= run("update").finally(() => {
19333
+ updatePromise = null;
19334
+ });
19335
+ return updatePromise;
19336
+ };
19337
+ update.addEventListener("click", () => void updateVersion());
19184
19338
  const owner = document2.defaultView;
19185
19339
  let checkFrame = null;
19186
19340
  let checkTimer = null;
@@ -19215,7 +19369,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19215
19369
  get updating() {
19216
19370
  return updating;
19217
19371
  },
19218
- update: () => void run("update")
19372
+ update: updateVersion
19219
19373
  };
19220
19374
  }
19221
19375
 
@@ -19246,6 +19400,58 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19246
19400
  };
19247
19401
  }
19248
19402
 
19403
+ // src/settings/connection-updates.ts
19404
+ function createConnectionUpdates(document2, messages, signal, onChange) {
19405
+ const batches = /* @__PURE__ */ new Map();
19406
+ const eligible = (panels) => panels.filter((panel) => panel.state?.updateAvailable && panel.state.canUpdate);
19407
+ const start = async (hostId2, panels) => {
19408
+ if (signal.aborted || batches.get(hostId2)?.running) return;
19409
+ const targets = eligible(panels);
19410
+ if (!targets.length) return;
19411
+ const batch = { running: true, total: targets.length, succeeded: 0, failed: 0 };
19412
+ batches.set(hostId2, batch);
19413
+ onChange();
19414
+ await Promise.all(
19415
+ targets.map(async (panel) => {
19416
+ try {
19417
+ if (await panel.update()) batch.succeeded++;
19418
+ else batch.failed++;
19419
+ } catch {
19420
+ batch.failed++;
19421
+ }
19422
+ if (!signal.aborted) onChange();
19423
+ })
19424
+ );
19425
+ batch.running = false;
19426
+ if (!signal.aborted) onChange();
19427
+ };
19428
+ return {
19429
+ createControls(hostId2, panels) {
19430
+ const batch = batches.get(hostId2);
19431
+ const count3 = eligible(panels).length;
19432
+ const button2 = document2.createElement("button");
19433
+ button2.type = "button";
19434
+ button2.className = "settings-command-button settings-command-button--secondary settings-connection-update-all";
19435
+ button2.dataset.connectionAction = "update-all";
19436
+ button2.disabled = signal.aborted || Boolean(batch?.running) || !count3;
19437
+ button2.title = messages.harnessUpdateAllDescription;
19438
+ const label = batch?.running ? `${messages.harnessUpdateAllProgress} ${batch.succeeded + batch.failed}/${batch.total}` : count3 ? `${messages.harnessUpdateAll} (${count3})` : messages.harnessUpdateAll;
19439
+ button2.append(createRendererSettingsIcon("download", 15), label);
19440
+ button2.addEventListener("click", () => {
19441
+ if (!button2.disabled) void start(hostId2, panels);
19442
+ });
19443
+ const result = document2.createElement("p");
19444
+ result.className = "settings-connection-update-result";
19445
+ result.setAttribute("role", "status");
19446
+ result.hidden = !batch || batch.running;
19447
+ if (batch && !batch.running) {
19448
+ result.textContent = messages.harnessUpdateAllResult.replace("{succeeded}", String(batch.succeeded)).replace("{failed}", String(batch.failed));
19449
+ }
19450
+ return { button: button2, result };
19451
+ }
19452
+ };
19453
+ }
19454
+
19249
19455
  // src/settings/connections-page.ts
19250
19456
  var CODEXHOST_GITHUB_ISSUES_NEW_URL = "https://github.com/karlorz/codex-host/issues/new";
19251
19457
  function connectionStatusLabel(availability, messages, hasError = false) {
@@ -19412,13 +19618,6 @@ ${error51.stderrTail}`] : []
19412
19618
  status.setAttribute("role", "cell");
19413
19619
  status.textContent = item.error?.code === "authenticationRequired" ? messages.connectionLoginRequired : item.error?.code === "configurationRequired" ? messages.connectionConfigurationRequired : connectionStatusLabel(item.availability, messages, item.error !== null);
19414
19620
  const version2 = item.version;
19415
- if (version2?.state?.updateAvailable && !version2.updating) {
19416
- const available = document2.createElement("span");
19417
- available.className = "settings-connection-version-available";
19418
- available.textContent = `${messages.harnessVersionAvailable}: ${harnessVersionLabel(version2.state, messages.harnessVersion)}`;
19419
- available.title = `${messages.harnessVersionCurrent}: ${version2.state.currentVersion ?? "\u2014"}`;
19420
- status.append(available);
19421
- }
19422
19621
  const action = document2.createElement("div");
19423
19622
  action.className = "settings-connection-row__action";
19424
19623
  action.setAttribute("role", "cell");
@@ -19448,6 +19647,14 @@ ${error51.stderrTail}`] : []
19448
19647
  });
19449
19648
  action.append(viewError);
19450
19649
  }
19650
+ if (version2?.state?.updateAvailable && !version2.updating) {
19651
+ const available = document2.createElement("span");
19652
+ available.className = "settings-connection-version-available";
19653
+ available.textContent = messages.harnessVersionAvailable;
19654
+ available.title = `${messages.harnessVersionCurrent}: ${version2.state.currentVersion ?? "\u2014"}
19655
+ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages.harnessVersion)}`;
19656
+ action.append(available);
19657
+ }
19451
19658
  if (version2?.state?.updateAvailable && version2.state.canUpdate) {
19452
19659
  const update = document2.createElement("button");
19453
19660
  update.type = "button";
@@ -19460,7 +19667,7 @@ ${error51.stderrTail}`] : []
19460
19667
  update.addEventListener("click", (event) => {
19461
19668
  event.stopPropagation();
19462
19669
  if (update.disabled) return;
19463
- version2.update();
19670
+ void version2.update();
19464
19671
  select();
19465
19672
  });
19466
19673
  action.append(update);
@@ -19802,6 +20009,7 @@ ${error51.stderrTail}`] : []
19802
20009
  scheduleRender
19803
20010
  );
19804
20011
  const installs = diagnostics ? harnessInstallStore(diagnostics) : null;
20012
+ const updates = createConnectionUpdates(document2, messages, context.signal, scheduleRender);
19805
20013
  const runRefresh = () => {
19806
20014
  if (pending || !diagnostics) return;
19807
20015
  pending = true;
@@ -20136,7 +20344,14 @@ ${error51.stderrTail}`] : []
20136
20344
  }
20137
20345
  const selectedItem = items.find((item) => item.key === selectedItemKey) ?? items[0];
20138
20346
  if (selectedItem) selectItem(selectedItem);
20139
- list.append(hostStrip, tableHeader, rows);
20347
+ const updateControls = updates.createControls(
20348
+ selectedHost.hostId,
20349
+ items.flatMap((item) => item.version ? [item.version] : [])
20350
+ );
20351
+ const hostHeader = document2.createElement("div");
20352
+ hostHeader.className = "settings-connection-host-header";
20353
+ hostHeader.append(hostStrip, updateControls.button);
20354
+ list.append(hostHeader, updateControls.result, tableHeader, rows);
20140
20355
  layout.append(list, inspector);
20141
20356
  content.replaceChildren(layout);
20142
20357
  disposeHostScroller = configureHostScroller(
@@ -20469,11 +20684,17 @@ ${error51.stderrTail}`] : []
20469
20684
  connectionInstallRunning: "Installing on the selected Host. You can leave this page; login or setup may still be required afterward.",
20470
20685
  harnessVersion: harnessVersionEnglish,
20471
20686
  harnessVersionTitle: "Harness CLI version",
20687
+ harnessVersionApplicationTitle: "Application version",
20472
20688
  harnessVersionCurrent: "Current version",
20473
20689
  harnessVersionLatest: "Latest version",
20474
20690
  harnessVersionChecking: "Checking versions\u2026",
20475
20691
  harnessVersionAvailable: "Update available",
20476
20692
  harnessVersionUpdate: "Update",
20693
+ harnessVersionDownloadUpdate: "Download update",
20694
+ harnessUpdateAll: "Update all",
20695
+ harnessUpdateAllProgress: "Updating",
20696
+ harnessUpdateAllDescription: "Update all eligible Harnesses on the selected Host.",
20697
+ harnessUpdateAllResult: "Updated {succeeded}; failed {failed}.",
20477
20698
  harnessVersionUpdating: "Updating\u2026",
20478
20699
  harnessVersionUpToDate: "Up to date",
20479
20700
  harnessVersionUpdated: "Update verified. New sessions will use the new version.",
@@ -20772,11 +20993,17 @@ ${error51.stderrTail}`] : []
20772
20993
  connectionInstallRunning: "\u6B63\u5728\u6240\u9009 Host \u4E0A\u5B89\u88C5\uFF0C\u79BB\u5F00\u9875\u9762\u4E0D\u4F1A\u4E2D\u65AD\u3002\u5B89\u88C5\u540E\u53EF\u80FD\u4ECD\u9700\u767B\u5F55\u6216\u914D\u7F6E\u3002",
20773
20994
  harnessVersion: harnessVersionChinese,
20774
20995
  harnessVersionTitle: "Harness CLI \u7248\u672C",
20996
+ harnessVersionApplicationTitle: "\u5E94\u7528\u7248\u672C",
20775
20997
  harnessVersionCurrent: "\u5F53\u524D\u7248\u672C",
20776
20998
  harnessVersionLatest: "\u6700\u65B0\u7248\u672C",
20777
20999
  harnessVersionChecking: "\u6B63\u5728\u68C0\u67E5\u7248\u672C\u2026",
20778
- harnessVersionAvailable: "\u6709\u65B0\u7248\u672C",
21000
+ harnessVersionAvailable: "\u6709\u66F4\u65B0",
20779
21001
  harnessVersionUpdate: "\u66F4\u65B0",
21002
+ harnessVersionDownloadUpdate: "\u4E0B\u8F7D\u66F4\u65B0",
21003
+ harnessUpdateAll: "\u5168\u90E8\u66F4\u65B0",
21004
+ harnessUpdateAllProgress: "\u66F4\u65B0\u4E2D",
21005
+ harnessUpdateAllDescription: "\u66F4\u65B0\u5F53\u524D Host \u4E0A\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u4E14\u6709\u65B0\u7248\u672C\u7684 Harness\u3002",
21006
+ harnessUpdateAllResult: "\u5DF2\u66F4\u65B0 {succeeded} \u9879\uFF0C{failed} \u9879\u5931\u8D25\u3002",
20780
21007
  harnessVersionUpdating: "\u6B63\u5728\u66F4\u65B0\u2026",
20781
21008
  harnessVersionUpToDate: "\u5DF2\u662F\u6700\u65B0",
20782
21009
  harnessVersionUpdated: "\u5DF2\u786E\u8BA4\u66F4\u65B0\u6210\u529F\uFF0C\u65B0\u4F1A\u8BDD\u5C06\u4F7F\u7528\u65B0\u7248\u672C\u3002",
@@ -23923,7 +24150,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23923
24150
  })
23924
24151
  );
23925
24152
  }
23926
- if (label) label.textContent = percent2;
24153
+ if (label) label.textContent = `${Math.round(remaining)}%`;
23927
24154
  control.root.style.display = "inline-flex";
23928
24155
  control.trigger.setAttribute("aria-label", title);
23929
24156
  control.trigger.title = title;
@@ -23931,6 +24158,148 @@ Codex Fast mode prioritizes requests and may increase usage.`
23931
24158
  return true;
23932
24159
  }
23933
24160
 
24161
+ // src/renderer-composer-indicators.ts
24162
+ var SHOW_WITHIN_SECONDS = 50 * 60;
24163
+ function mountRendererComposerIndicators(composer) {
24164
+ const doc = composer.ownerDocument;
24165
+ ensureRendererTriggerChipStyle(doc);
24166
+ const root = doc.createElement("div");
24167
+ root.setAttribute("data-codexhost-composer-indicators", "true");
24168
+ applyRendererTriggerChipSqueezeRoot(root, "128px", "0");
24169
+ Object.assign(root.style, { alignItems: "center", gap: "2px", flexShrink: "100000" });
24170
+ let indicators = [];
24171
+ let locale = "en";
24172
+ let timer;
24173
+ let anchor = null;
24174
+ let contentKey = "";
24175
+ let tip = null;
24176
+ const hideTip = () => {
24177
+ tip?.remove();
24178
+ tip = null;
24179
+ };
24180
+ const showTip = (chip, textContent) => {
24181
+ hideTip();
24182
+ const el = doc.createElement("div");
24183
+ el.setAttribute("role", "tooltip");
24184
+ el.textContent = textContent;
24185
+ Object.assign(el.style, {
24186
+ position: "fixed",
24187
+ zIndex: "2147483647",
24188
+ pointerEvents: "none",
24189
+ padding: "4px 8px",
24190
+ borderRadius: "8px",
24191
+ fontSize: "12px",
24192
+ whiteSpace: "nowrap",
24193
+ background: "rgba(30, 30, 30, 0.92)",
24194
+ color: "#fff",
24195
+ visibility: "hidden"
24196
+ });
24197
+ (doc.body ?? doc.documentElement).append(el);
24198
+ const rect = chip.getBoundingClientRect();
24199
+ const width = el.offsetWidth;
24200
+ const left = Math.max(
24201
+ 4,
24202
+ Math.min(rect.left + rect.width / 2 - width / 2, window.innerWidth - width - 4)
24203
+ );
24204
+ el.style.left = `${left}px`;
24205
+ el.style.top = `${Math.max(4, rect.top - el.offsetHeight - 6)}px`;
24206
+ el.style.visibility = "visible";
24207
+ tip = el;
24208
+ };
24209
+ const refresh = () => {
24210
+ const now = Date.now() / 1e3;
24211
+ const active = indicators.filter(
24212
+ (indicator) => indicator.expiresAtUnix > now && indicator.expiresAtUnix - now <= SHOW_WITHIN_SECONDS
24213
+ );
24214
+ const nextKey = JSON.stringify([
24215
+ locale,
24216
+ active.map((indicator) => [indicator, Math.ceil((indicator.expiresAtUnix - now) / 60)])
24217
+ ]);
24218
+ if (nextKey !== contentKey) {
24219
+ contentKey = nextKey;
24220
+ hideTip();
24221
+ root.replaceChildren();
24222
+ for (const indicator of active) {
24223
+ const minutes = Math.ceil((indicator.expiresAtUnix - now) / 60);
24224
+ const remaining = locale === "zh-CN" ? `\u5269\u4F59 ${minutes} \u5206\u949F` : `${minutes}m remaining`;
24225
+ const label = indicator.label ? localizeHarnessText(indicator.label, locale) : "";
24226
+ const tooltip = indicator.tooltip ? localizeHarnessText(indicator.tooltip, locale) : "";
24227
+ const chip = doc.createElement("span");
24228
+ chip.className = TRIGGER_CHIP_CLASS;
24229
+ chip.setAttribute("data-indicator-id", indicator.id);
24230
+ const summary = [label, tooltip, remaining].filter(Boolean).join(" \xB7 ");
24231
+ chip.setAttribute("aria-label", summary);
24232
+ chip.addEventListener("mouseenter", () => showTip(chip, summary));
24233
+ chip.addEventListener("mouseleave", hideTip);
24234
+ Object.assign(chip.style, {
24235
+ height: "28px",
24236
+ padding: "0 6px",
24237
+ gap: "4px",
24238
+ fontSize: "12px",
24239
+ cursor: "default",
24240
+ flexShrink: "0"
24241
+ });
24242
+ const icon2 = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
24243
+ icon2.setAttribute("viewBox", "0 0 24 24");
24244
+ icon2.setAttribute("width", "14");
24245
+ icon2.setAttribute("height", "14");
24246
+ icon2.setAttribute("fill", "none");
24247
+ icon2.setAttribute("stroke", "currentColor");
24248
+ icon2.setAttribute("stroke-width", "1.5");
24249
+ icon2.setAttribute("aria-hidden", "true");
24250
+ const path = doc.createElementNS("http://www.w3.org/2000/svg", "path");
24251
+ path.setAttribute("d", "M12 8v4l3 2M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0");
24252
+ icon2.append(path);
24253
+ const text = doc.createElement("span");
24254
+ text.textContent = `${minutes}m`;
24255
+ chip.append(icon2, text);
24256
+ root.append(chip);
24257
+ }
24258
+ }
24259
+ const visible = active.length > 0 && anchor !== null;
24260
+ if (root.hidden === visible) root.hidden = !visible;
24261
+ const display = visible ? "inline-flex" : "none";
24262
+ if (root.style.display !== display) root.style.display = display;
24263
+ const visibility = root.scrollWidth > root.clientWidth ? "hidden" : "visible";
24264
+ if (root.style.visibility !== visibility) root.style.visibility = visibility;
24265
+ const pending = indicators.some(({ expiresAtUnix }) => expiresAtUnix > now);
24266
+ if (!pending && timer !== void 0) {
24267
+ clearInterval(timer);
24268
+ timer = void 0;
24269
+ }
24270
+ };
24271
+ const observer = new ResizeObserver(refresh);
24272
+ observer.observe(root);
24273
+ refresh();
24274
+ return {
24275
+ root,
24276
+ place(next) {
24277
+ anchor = next;
24278
+ if (!next?.parentElement) {
24279
+ anchor = null;
24280
+ root.remove();
24281
+ } else if (next.nextElementSibling !== root) {
24282
+ next.parentElement.insertBefore(root, next.nextSibling);
24283
+ }
24284
+ refresh();
24285
+ },
24286
+ render(next, nextLocale) {
24287
+ indicators = next;
24288
+ locale = nextLocale;
24289
+ refresh();
24290
+ if (timer === void 0 && indicators.some(({ expiresAtUnix }) => expiresAtUnix > Date.now() / 1e3)) {
24291
+ timer = setInterval(refresh, 6e4);
24292
+ }
24293
+ },
24294
+ dispose() {
24295
+ observer.disconnect();
24296
+ hideTip();
24297
+ if (timer !== void 0) clearInterval(timer);
24298
+ root.remove();
24299
+ }
24300
+ };
24301
+ }
24302
+
23934
24303
  // src/renderer-harness-command-control.ts
23935
24304
  var CONTROL_ATTRIBUTE2 = "data-codexhost-harness-command-control";
23936
24305
  var SVG_NS2 = "http://www.w3.org/2000/svg";
@@ -24084,7 +24453,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24084
24453
  return controlDescription(button2);
24085
24454
  }
24086
24455
  function isOwnedRendererControl(element) {
24087
- return element.hasAttribute(CONTROL_ATTRIBUTE) || element.hasAttribute("data-codexhost-model-control") || element.hasAttribute("data-codexhost-permission-mode-control") || element.hasAttribute("data-codexhost-usage-control") || element.hasAttribute("data-codexhost-credits-control") || element.hasAttribute("data-codexhost-harness-command-control");
24456
+ return element.hasAttribute(CONTROL_ATTRIBUTE) || element.hasAttribute("data-codexhost-model-control") || element.hasAttribute("data-codexhost-permission-mode-control") || element.hasAttribute("data-codexhost-usage-control") || element.hasAttribute("data-codexhost-credits-control") || element.hasAttribute("data-codexhost-composer-indicators") || element.hasAttribute("data-codexhost-harness-command-control");
24088
24457
  }
24089
24458
  function isComposerSubmitButton(button2) {
24090
24459
  if (button2.type === "submit") return true;
@@ -24400,6 +24769,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
24400
24769
  refreshTrailingClusterPlacement(control);
24401
24770
  refreshUsagePlacement(control);
24402
24771
  refreshCreditsPlacement(control);
24772
+ control.composerIndicators?.place(
24773
+ control.nativePermissionModeControlVerified ? control.nativePermissionModeControl?.element ?? null : null
24774
+ );
24403
24775
  setNativeControlHidden(control.nativeModelControl, hideModel);
24404
24776
  setNativeControlHidden(control.nativeContextUsageControl, false);
24405
24777
  setNativeControlHidden(control.nativePermissionModeControl, hidePermissionMode);
@@ -24434,6 +24806,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24434
24806
  onSelectPermissionMode
24435
24807
  );
24436
24808
  const credits = mountRendererCreditsControl(composerId);
24809
+ const composerIndicators = mountRendererComposerIndicators(composer);
24437
24810
  const toolbar = sendButton.parentElement;
24438
24811
  const harnessCommands = mountRendererHarnessCommandControl(
24439
24812
  toolbar ?? composer,
@@ -24481,6 +24854,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24481
24854
  nativeUsageBanner,
24482
24855
  nativePermissionModeControlVerified,
24483
24856
  credits,
24857
+ composerIndicators,
24484
24858
  usage: null,
24485
24859
  harnessCommands,
24486
24860
  sendButton,
@@ -24546,6 +24920,10 @@ Codex Fast mode prioritizes requests and may increase usage.`
24546
24920
  permissionModeVisible,
24547
24921
  locale
24548
24922
  );
24923
+ control.composerIndicators?.render(
24924
+ permissionModeVisible ? usage?.composerIndicators ?? [] : [],
24925
+ locale
24926
+ );
24549
24927
  if (control.usage) {
24550
24928
  renderRendererUsageControl(control.usage, usage, locale);
24551
24929
  }
@@ -24566,6 +24944,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24566
24944
  restoreNativeControl(control.nativeContextUsageControl);
24567
24945
  restoreNativeControl(control.nativeUsageBanner);
24568
24946
  restoreNativeControl(control.nativePermissionModeControl);
24947
+ control.composerIndicators?.dispose();
24569
24948
  control.credits.dispose();
24570
24949
  control.usage?.dispose();
24571
24950
  control.usage = null;
@@ -25122,6 +25501,26 @@ Codex Fast mode prioritizes requests and may increase usage.`
25122
25501
  };
25123
25502
  }
25124
25503
 
25504
+ // src/renderer-installation-client.ts
25505
+ async function runRendererInstallation(send, input) {
25506
+ const params = harnessInstallationParamsSchema.parse(input);
25507
+ const options = { priority: "background", timeoutMs: 15e3 };
25508
+ let task = harnessInstallationTaskSchema.parse(
25509
+ await send(HARNESS_INSTALLATION_START_METHOD, params, options)
25510
+ );
25511
+ const taskId = task.taskId;
25512
+ while (task.status === "running") {
25513
+ await new Promise((resolve) => globalThis.setTimeout(resolve, 500));
25514
+ task = harnessInstallationTaskSchema.parse(
25515
+ await send(HARNESS_INSTALLATION_STATUS_METHOD, { taskId }, options)
25516
+ );
25517
+ if (task.taskId !== taskId) throw new Error("Harness installation task identity changed");
25518
+ }
25519
+ if (task.status === "failed")
25520
+ throw Object.assign(new Error(task.error.message), { code: task.error.code });
25521
+ return task.result;
25522
+ }
25523
+
25125
25524
  // src/renderer-request-sender.ts
25126
25525
  var RendererMethodUnavailableError = class extends Error {
25127
25526
  constructor(method, cause) {
@@ -25359,13 +25758,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
25359
25758
  };
25360
25759
  return Object.freeze({
25361
25760
  async installation(input) {
25362
- return harnessInstallationStateSchema.parse(
25363
- await manager.sendRequest(
25364
- HARNESS_INSTALLATION_METHOD,
25365
- harnessInstallationParamsSchema.parse(input),
25366
- { priority: "interactive" }
25367
- )
25368
- );
25761
+ return runRendererInstallation(manager.sendRequest, input);
25369
25762
  },
25370
25763
  async getHarnessDisplaySettings() {
25371
25764
  return harnessDisplaySettingsSchema.parse(
@@ -25632,6 +26025,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
25632
26025
  }
25633
26026
 
25634
26027
  // src/renderer-external-steering.ts
26028
+ var THREAD_STEERING_INSPECT_METHOD = "codexhost/thread/steering/inspect";
25635
26029
  var THREAD_OWNERSHIP_LIST_METHOD2 = "codexhost/thread/ownership/list";
25636
26030
  function isRecord9(value) {
25637
26031
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -25715,6 +26109,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
25715
26109
  const originalSteer = manager.steerTurn;
25716
26110
  const originalSend = manager.sendRequest;
25717
26111
  const routes = /* @__PURE__ */ new Map();
26112
+ const activeTurnSteers = /* @__PURE__ */ new Map();
25718
26113
  const pending = /* @__PURE__ */ new Map();
25719
26114
  let disposed = false;
25720
26115
  const send = function(method, params, options) {
@@ -25730,6 +26125,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
25730
26125
  return originalSend.call(manager, method, input, options);
25731
26126
  });
25732
26127
  }
26128
+ if (method === "turn/steer" && isRecord9(params) && typeof params.threadId === "string" && activeTurnSteers.has(params.threadId)) {
26129
+ params = { ...params, [THREAD_STEER_DELIVERY_PARAM]: "activeTurn" };
26130
+ }
25733
26131
  return originalSend.call(manager, method, params, options);
25734
26132
  }
25735
26133
  if (disposed) return Promise.reject(new Error("External steering binding was disposed"));
@@ -25744,7 +26142,8 @@ Codex Fast mode prioritizes requests and may increase usage.`
25744
26142
  input: params.input,
25745
26143
  additionalContext: params.additionalContext,
25746
26144
  responsesapiClientMetadata: params.responsesapiClientMetadata,
25747
- ...params[THREAD_REFERENCE_SOURCE_HOST_PARAM] !== void 0 ? { [THREAD_REFERENCE_SOURCE_HOST_PARAM]: params[THREAD_REFERENCE_SOURCE_HOST_PARAM] } : {}
26145
+ ...params[THREAD_REFERENCE_SOURCE_HOST_PARAM] !== void 0 ? { [THREAD_REFERENCE_SOURCE_HOST_PARAM]: params[THREAD_REFERENCE_SOURCE_HOST_PARAM] } : {},
26146
+ [THREAD_STEER_DELIVERY_PARAM]: "newTurn"
25748
26147
  },
25749
26148
  options
25750
26149
  )
@@ -25766,6 +26165,34 @@ Codex Fast mode prioritizes requests and may increase usage.`
25766
26165
  };
25767
26166
  });
25768
26167
  };
26168
+ const inspectDelivery = async (threadId, text) => {
26169
+ try {
26170
+ const response = await originalSend.call(manager, THREAD_STEERING_INSPECT_METHOD, {
26171
+ threadId,
26172
+ ...text !== void 0 ? { text } : {}
26173
+ });
26174
+ return threadSteeringInspectResultSchema.parse(response).delivery;
26175
+ } catch (error51) {
26176
+ if (!isUnsupportedMethod(error51, THREAD_STEERING_INSPECT_METHOD)) throw error51;
26177
+ }
26178
+ let ownershipResponse;
26179
+ try {
26180
+ ownershipResponse = await originalSend.call(manager, THREAD_OWNERSHIP_LIST_METHOD2, {
26181
+ threadIds: [threadId]
26182
+ });
26183
+ } catch (error51) {
26184
+ if (!isUnsupportedMethod(error51, THREAD_OWNERSHIP_LIST_METHOD2)) throw error51;
26185
+ await verifyNativeCodexThread(
26186
+ (method, params) => originalSend.call(manager, method, params),
26187
+ threadId
26188
+ );
26189
+ return "official";
26190
+ }
26191
+ const ownership = threadOwnershipListResultSchema.parse(ownershipResponse);
26192
+ const owner = ownership.threads.find((thread) => thread.threadId === threadId)?.owner;
26193
+ if (!owner) throw new Error("Thread ownership could not be resolved for steering");
26194
+ return owner === "codex" ? "official" : "newTurn";
26195
+ };
25769
26196
  const steer = async function(...args) {
25770
26197
  const [
25771
26198
  threadId,
@@ -25788,25 +26215,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
25788
26215
  expectedTurnId = host.getActiveTurnId(threadId);
25789
26216
  } catch {
25790
26217
  }
25791
- let ownershipResponse;
25792
- try {
25793
- ownershipResponse = await originalSend.call(manager, THREAD_OWNERSHIP_LIST_METHOD2, {
25794
- threadIds: [threadId]
25795
- });
25796
- } catch (error51) {
25797
- if (!isUnsupportedMethod(error51, THREAD_OWNERSHIP_LIST_METHOD2)) throw error51;
25798
- await verifyNativeCodexThread(
25799
- (method, params) => originalSend.call(manager, method, params),
25800
- threadId
25801
- );
25802
- if (disposed) throw new Error("External steering binding was disposed");
25803
- return originalSteer.apply(manager, args);
25804
- }
25805
- const ownership = threadOwnershipListResultSchema.parse(ownershipResponse);
25806
- const owner = ownership.threads.find((thread) => thread.threadId === threadId)?.owner;
25807
- if (!owner) throw new Error("Thread ownership could not be resolved for steering");
26218
+ const text = Array.isArray(input) ? input.filter((item) => isRecord9(item) && typeof item.text === "string").map((item) => item.text).join("\n") : void 0;
26219
+ const delivery = await inspectDelivery(threadId, text);
25808
26220
  if (disposed) throw new Error("External steering binding was disposed");
25809
- if (owner === "codex") return originalSteer.apply(manager, args);
26221
+ if (delivery === "official") return originalSteer.apply(manager, args);
26222
+ if (delivery === "activeTurn") {
26223
+ activeTurnSteers.set(threadId, (activeTurnSteers.get(threadId) ?? 0) + 1);
26224
+ try {
26225
+ return await originalSteer.apply(manager, args);
26226
+ } finally {
26227
+ const remaining = (activeTurnSteers.get(threadId) ?? 1) - 1;
26228
+ if (remaining > 0) activeTurnSteers.set(threadId, remaining);
26229
+ else activeTurnSteers.delete(threadId);
26230
+ }
26231
+ }
25810
26232
  if (!host) throw new Error("Desktop turn submission binding is unavailable");
25811
26233
  const currentRole = manager.getStreamRole?.(threadId);
25812
26234
  if (isRecord9(currentRole) && currentRole.role === "follower")
@@ -26051,9 +26473,9 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
26051
26473
  }
26052
26474
 
26053
26475
  // src/renderer-thread-reference-capability.ts
26054
- var record4 = (value) => typeof value === "object" && value !== null;
26476
+ var record5 = (value) => typeof value === "object" && value !== null;
26055
26477
  function restoreThreadReferenceCapability(client, manager, isCurrent) {
26056
- if (!record4(manager) || !record4(manager.storage)) return client;
26478
+ if (!record5(manager) || !record5(manager.storage)) return client;
26057
26479
  const storage = manager.storage;
26058
26480
  const read = storage.readValue;
26059
26481
  const write = storage.writeValue;
@@ -26088,14 +26510,14 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
26088
26510
  }
26089
26511
 
26090
26512
  // src/renderer-thread-reference-context.ts
26091
- var record5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26513
+ var record6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26092
26514
  function createThreadReferenceContext(manager, hostId2, isCurrent) {
26093
26515
  const prepared = /* @__PURE__ */ new WeakMap();
26094
26516
  return (params) => {
26095
- if (hostId2 === "local" || !record5(manager) || typeof manager.getConversation !== "function" || !record5(params) || typeof params.threadId !== "string")
26517
+ if (hostId2 === "local" || !record6(manager) || typeof manager.getConversation !== "function" || !record6(params) || typeof params.threadId !== "string")
26096
26518
  return params;
26097
26519
  const conversation = manager.getConversation.call(manager, params.threadId);
26098
- if (!record5(conversation) || conversation.id !== params.threadId || conversation.modelProvider !== "codexhost")
26520
+ if (!record6(conversation) || conversation.id !== params.threadId || conversation.modelProvider !== "codexhost")
26099
26521
  return params;
26100
26522
  if (!isCurrent()) throw new Error("Thread reference connection is no longer available");
26101
26523
  const cached2 = prepared.get(params);
@@ -27507,6 +27929,11 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27507
27929
  var GET_LOCALE_OVERRIDE_URL = "vscode://codex/get-setting";
27508
27930
  var GET_LOCALE_INFO_URL = "vscode://codex/locale-info";
27509
27931
  var DEFAULT_LOCALE_REQUEST_TIMEOUT_MS = 750;
27932
+ function isNativeLocaleServices(value) {
27933
+ if (!isRecord15(value) || !Object.hasOwn(value, "appInfo")) return false;
27934
+ const appInfo = value.appInfo;
27935
+ return appInfo != null && typeof appInfo.getLocaleInfo === "function";
27936
+ }
27510
27937
  function isRecord15(value) {
27511
27938
  return typeof value === "object" && value !== null && !Array.isArray(value);
27512
27939
  }
@@ -27552,7 +27979,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27552
27979
  }
27553
27980
  function sendFixedLocaleRequest(request, ownerWindow, signal, timeoutMs) {
27554
27981
  const bridge = ownerWindow.electronBridge;
27555
- if (typeof bridge?.sendMessageFromView !== "function") {
27982
+ if (request.kind !== "locale-info" && typeof bridge?.sendMessageFromView !== "function") {
27556
27983
  return Promise.reject(new Error("Codex locale bridge is unavailable"));
27557
27984
  }
27558
27985
  if (signal?.aborted) return Promise.reject(abortError2());
@@ -27600,13 +28027,21 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27600
28027
  ...request.body === void 0 ? {} : { body: request.body },
27601
28028
  reportUploadProgress: false
27602
28029
  };
27603
- try {
27604
- Promise.resolve(bridge.sendMessageFromView(message2)).catch((error51) => {
27605
- settle(() => reject(error51));
27606
- });
27607
- } catch (error51) {
27608
- settle(() => reject(error51));
27609
- }
28030
+ void (async () => {
28031
+ if (request.kind === "locale-info") {
28032
+ const native = await loadCodexNativeServices(ownerWindow, isNativeLocaleServices);
28033
+ if (settled) return;
28034
+ if (native) {
28035
+ const response = await native.appInfo.getLocaleInfo();
28036
+ settle(() => resolve(response));
28037
+ return;
28038
+ }
28039
+ }
28040
+ if (typeof bridge?.sendMessageFromView !== "function") {
28041
+ throw new Error("Codex locale bridge is unavailable");
28042
+ }
28043
+ await bridge.sendMessageFromView(message2);
28044
+ })().catch((error51) => settle(() => reject(error51)));
27610
28045
  });
27611
28046
  }
27612
28047
  async function readLocaleOverride(ownerWindow, signal, timeoutMs) {
@@ -28167,6 +28602,34 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
28167
28602
  return names.size === 1 ? names.values().next().value ?? model2 : model2;
28168
28603
  }
28169
28604
 
28605
+ // src/console/usage/usage-hosts.ts
28606
+ var LOCAL_USAGE_HOST_ID = "local";
28607
+ function usageHostOptions(hosts, localLabel, selected) {
28608
+ const options = [{ key: LOCAL_USAGE_HOST_ID, label: localLabel }];
28609
+ const seen = /* @__PURE__ */ new Set([LOCAL_USAGE_HOST_ID]);
28610
+ const add = (hostId2, label) => {
28611
+ if (hostId2.length === 0 || seen.has(hostId2)) return;
28612
+ seen.add(hostId2);
28613
+ options.push({ key: hostId2, label: label !== void 0 && label.length > 0 ? label : hostId2 });
28614
+ };
28615
+ for (const host of hosts ?? []) add(host.hostId, host.label);
28616
+ if (!seen.has(selected)) add(selected, void 0);
28617
+ return options;
28618
+ }
28619
+ function createLatestKeyGuard() {
28620
+ let generation = 0;
28621
+ let key;
28622
+ return {
28623
+ begin(next) {
28624
+ generation += 1;
28625
+ key = next;
28626
+ const mine = generation;
28627
+ const selected = next;
28628
+ return () => generation === mine && Object.is(key, selected);
28629
+ }
28630
+ };
28631
+ }
28632
+
28170
28633
  // src/console/usage/format.ts
28171
28634
  var MEASURES = ["cost", "tokens"];
28172
28635
  function fill(template, values) {
@@ -29453,6 +29916,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29453
29916
  const range = text("range");
29454
29917
  const measure = text("measure");
29455
29918
  const granularity = text("granularity");
29919
+ const hostId2 = text("hostId");
29456
29920
  const harness2 = text("harness");
29457
29921
  const model2 = text("model");
29458
29922
  const project3 = text("project");
@@ -29461,6 +29925,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29461
29925
  ...range && USAGE_STATISTICS_RANGES.includes(range) ? { range } : {},
29462
29926
  ...measure && MEASURES.includes(measure) ? { measure } : {},
29463
29927
  ...granularity === "day" || granularity === "week" || granularity === "month" ? { granularity } : {},
29928
+ ...hostId2 !== void 0 && hostId2.length > 0 ? { hostId: hostId2 } : {},
29464
29929
  ...harness2 !== void 0 ? { harness: harness2 } : {},
29465
29930
  ...model2 !== void 0 ? { model: model2 } : {},
29466
29931
  ...project3 !== void 0 ? { project: project3 } : {},
@@ -29499,7 +29964,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29499
29964
  day.setDate(day.getDate() - (day.getDay() + 6) % 7);
29500
29965
  return isoDate(day);
29501
29966
  }
29502
- function createUsageStatisticsPage(consoleMessages2, request, pageLocale) {
29967
+ function createUsageStatisticsPage(consoleMessages2, request, pageLocale, usageHosts) {
29503
29968
  const messages = consoleMessages2.usageStatistics;
29504
29969
  return Object.freeze({
29505
29970
  id: "usage-statistics",
@@ -29508,10 +29973,12 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29508
29973
  mount(context) {
29509
29974
  const document2 = context.content.ownerDocument;
29510
29975
  const locale = pageLocale ?? (document2.documentElement.lang || "en");
29976
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
29511
29977
  const count3 = (value) => count2(value, locale);
29512
29978
  const tokenCount2 = (totals2, value) => tokenCount(totals2, value, locale);
29513
29979
  const saved = readView();
29514
29980
  const view = {
29981
+ hostId: saved.hostId ?? LOCAL_USAGE_HOST_ID,
29515
29982
  range: saved.range ?? "30d",
29516
29983
  measure: saved.measure ?? "cost",
29517
29984
  granularity: saved.granularity ?? null,
@@ -29542,7 +30009,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29542
30009
  let projectLabel = /* @__PURE__ */ new Map();
29543
30010
  const projectName = (key) => key === UNKNOWN ? messages.unknownProject : projectLabel.get(key) ?? baseName(key);
29544
30011
  const measureLabel = (measure) => measure === "cost" ? messages.cost : messages.tokens;
29545
- const dateFormat = (options) => new Intl.DateTimeFormat(locale, options);
30012
+ const dateFormat = (options) => new Intl.DateTimeFormat(locale, { ...options, timeZone });
29546
30013
  const shortDate = dateFormat({ month: "short", day: "numeric" });
29547
30014
  const longDate = dateFormat({
29548
30015
  year: "numeric",
@@ -29561,10 +30028,51 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29561
30028
  const prices = createModelPricesDialog(
29562
30029
  document2,
29563
30030
  consoleMessages2,
29564
- request,
30031
+ (method, params2) => request(method, params2, view.hostId),
29565
30032
  context.signal,
29566
30033
  load
29567
30034
  );
30035
+ let offeredHosts = usageHosts ? [] : void 0;
30036
+ const hostCombo = new ComboBox(document2, {
30037
+ label: messages.host,
30038
+ searchLabel: messages.search,
30039
+ emptyLabel: messages.noMatches,
30040
+ signal: context.signal,
30041
+ searchable: false,
30042
+ display: (option) => `${messages.host} \xB7 ${option.label}`,
30043
+ onChange: (key) => {
30044
+ if (key === view.hostId) return;
30045
+ view.hostId = key;
30046
+ view.harness = "";
30047
+ view.model = "";
30048
+ view.project = "";
30049
+ date5 = null;
30050
+ unpricedOnly = false;
30051
+ result = null;
30052
+ error51 = null;
30053
+ loadedAt = 0;
30054
+ names.clear();
30055
+ save();
30056
+ hostOptions();
30057
+ render();
30058
+ void refreshHarnessNames();
30059
+ load();
30060
+ }
30061
+ });
30062
+ const hostOptions = () => {
30063
+ const options = usageHostOptions(offeredHosts, messages.localHost, view.hostId);
30064
+ hostCombo.element.hidden = options.length < 2;
30065
+ hostCombo.update(options, view.hostId === LOCAL_USAGE_HOST_ID ? "" : view.hostId);
30066
+ };
30067
+ const offerHosts = () => {
30068
+ if (!usageHosts) return;
30069
+ void usageHosts().then((hosts) => {
30070
+ if (context.signal.aborted) return;
30071
+ offeredHosts = hosts;
30072
+ hostOptions();
30073
+ }).catch(() => void 0);
30074
+ };
30075
+ hostOptions();
29568
30076
  const updated = h(document2, "span", { className: "console-muted console-usage-updated" });
29569
30077
  const refresh = h(
29570
30078
  document2,
@@ -29577,7 +30085,10 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29577
30085
  },
29578
30086
  createRendererSettingsIcon("refresh", 16)
29579
30087
  );
29580
- refresh.addEventListener("click", () => load());
30088
+ refresh.addEventListener("click", () => {
30089
+ offerHosts();
30090
+ load();
30091
+ });
29581
30092
  const auto = new ComboBox(document2, {
29582
30093
  label: messages.autoRefresh,
29583
30094
  searchLabel: messages.search,
@@ -29616,6 +30127,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
29616
30127
  document2,
29617
30128
  "div",
29618
30129
  { className: "console-usage-toolbar" },
30130
+ hostCombo.element,
29619
30131
  updated,
29620
30132
  refresh,
29621
30133
  auto.element,
@@ -30254,7 +30766,6 @@ ${detail}` : ""}`
30254
30766
  }
30255
30767
  function hourlyPanel(data) {
30256
30768
  const singleDay = data.filters.date !== null || data.range === "today";
30257
- const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
30258
30769
  return panel(
30259
30770
  singleDay ? messages.hourlyDay : messages.hourly,
30260
30771
  hourlyChart(document2, {
@@ -30269,7 +30780,7 @@ ${detail}` : ""}`
30269
30780
  weekStartsOnMonday: !locale.startsWith("en"),
30270
30781
  labels: { cost: messages.cost, tokens: messages.tokens }
30271
30782
  }),
30272
- h(document2, "span", { className: "console-muted console-usage-zone" }, zone)
30783
+ h(document2, "span", { className: "console-muted console-usage-zone" }, timeZone)
30273
30784
  );
30274
30785
  }
30275
30786
  function sessionsPanel(title, rows, tableKey) {
@@ -30416,6 +30927,7 @@ ${detail}` : ""}`
30416
30927
  const project3 = filterValue(view.project);
30417
30928
  return {
30418
30929
  range: view.range,
30930
+ timeZone,
30419
30931
  ...view.harness ? { harness: view.harness } : {},
30420
30932
  ...model2 !== void 0 ? { model: model2 } : {},
30421
30933
  ...project3 !== void 0 ? { project: project3 } : {},
@@ -30428,10 +30940,26 @@ ${detail}` : ""}`
30428
30940
  if (result && !result.reading.complete) poll = window.setTimeout(load, POLL_MS);
30429
30941
  else if (view.autoRefreshMs > 0) poll = window.setTimeout(load, view.autoRefreshMs);
30430
30942
  }
30943
+ const harnessNamesRead = createLatestKeyGuard();
30944
+ async function refreshHarnessNames() {
30945
+ const hostId2 = view.hostId;
30946
+ const isCurrent = harnessNamesRead.begin(hostId2);
30947
+ names.clear();
30948
+ try {
30949
+ const value = await request(HARNESS_PLUGIN_LIST_METHOD2, {}, hostId2);
30950
+ if (!isCurrent()) return;
30951
+ for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
30952
+ names.set(plugin.id, plugin.name);
30953
+ }
30954
+ } catch {
30955
+ if (!isCurrent()) return;
30956
+ }
30957
+ if (!context.signal.aborted) render();
30958
+ }
30431
30959
  function load() {
30432
30960
  window.clearTimeout(poll);
30433
30961
  const requested = params();
30434
- void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested), {
30962
+ void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested, view.hostId), {
30435
30963
  success(value) {
30436
30964
  const parsed = usageStatisticsResultSchema.safeParse(value);
30437
30965
  if (parsed.success) {
@@ -30456,12 +30984,8 @@ ${detail}` : ""}`
30456
30984
  }
30457
30985
  context.content.replaceChildren(header, prices.element, filters, notices, body);
30458
30986
  render();
30459
- void request(HARNESS_PLUGIN_LIST_METHOD2, {}).then((value) => {
30460
- for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
30461
- names.set(plugin.id, plugin.name);
30462
- }
30463
- if (!context.signal.aborted) render();
30464
- }).catch(() => void 0);
30987
+ offerHosts();
30988
+ void refreshHarnessNames();
30465
30989
  load();
30466
30990
  return () => {
30467
30991
  window.clearTimeout(poll);
@@ -30567,6 +31091,8 @@ ${detail}` : ""}`
30567
31091
  harness: "Harness",
30568
31092
  model: "Model",
30569
31093
  project: "Project",
31094
+ host: "Machine",
31095
+ localHost: "This machine",
30570
31096
  filterModel: "Filter by model",
30571
31097
  filterProject: "Filter by project",
30572
31098
  unknownModel: "Unknown model",
@@ -30763,6 +31289,8 @@ ${detail}` : ""}`
30763
31289
  harness: "Harness",
30764
31290
  model: "\u6A21\u578B",
30765
31291
  project: "\u9879\u76EE",
31292
+ host: "\u673A\u5668",
31293
+ localHost: "\u672C\u673A",
30766
31294
  filterModel: "\u6309\u6A21\u578B\u7B5B\u9009",
30767
31295
  filterProject: "\u6309\u9879\u76EE\u7B5B\u9009",
30768
31296
  unknownModel: "\u672A\u77E5\u6A21\u578B",
@@ -33160,7 +33688,7 @@ ${detail}` : ""}`
33160
33688
  }
33161
33689
  function defaultImportName(source, existing) {
33162
33690
  const base = source.provider === "openai-codex" ? "codex" : "grok";
33163
- const used = new Set(existing.map((record7) => record7.name));
33691
+ const used = new Set(existing.map((record8) => record8.name));
33164
33692
  const valid = (name) => credentialImportNameSchema.safeParse(name).success;
33165
33693
  if (!used.has(base)) return base;
33166
33694
  const local = source.label.includes("@") ? source.label.split("@")[0] ?? "" : "";
@@ -33183,7 +33711,7 @@ ${detail}` : ""}`
33183
33711
  const shown = (text) => accountDisplayText(text, hideEmails);
33184
33712
  const targetButtons = /* @__PURE__ */ new Map();
33185
33713
  const importedRecords = () => snapshot.targets.find((target) => target.harnessId === TARGET_HARNESS_ID)?.imports ?? [];
33186
- const sourceOf = (record7) => snapshot.sources.find((source) => source.id === record7.source.id);
33714
+ const sourceOf = (record8) => snapshot.sources.find((source) => source.id === record8.source.id);
33187
33715
  const piTarget = () => snapshot.targets.find((candidate) => candidate.harnessId === TARGET_HARNESS_ID);
33188
33716
  const compatibleSources = () => {
33189
33717
  const target = piTarget();
@@ -33221,7 +33749,7 @@ ${detail}` : ""}`
33221
33749
  sectionImport.addEventListener("click", () => {
33222
33750
  const sources = compatibleSources();
33223
33751
  const free = sources.find(
33224
- (source2) => !importedRecords().some((record7) => record7.source.id === source2.id)
33752
+ (source2) => !importedRecords().some((record8) => record8.source.id === source2.id)
33225
33753
  );
33226
33754
  const source = free ?? sources[0];
33227
33755
  if (source) openDialog("add", { source, choose: true }, SECTION_IMPORT_KEY);
@@ -33271,30 +33799,30 @@ ${detail}` : ""}`
33271
33799
  mark.append(createRendererAgentIcon(agent ?? "pi", 26, document2, pluginFor(agent ?? "pi")));
33272
33800
  return mark;
33273
33801
  };
33274
- const renderRow = (record7) => {
33802
+ const renderRow = (record8) => {
33275
33803
  const row = document2.createElement("div");
33276
33804
  row.className = "settings-pi-accounts__row";
33277
33805
  row.tabIndex = -1;
33278
- row.dataset.importName = record7.name;
33279
- row.setAttribute("aria-label", shown(record7.source.label));
33280
- const agent = record7.source.harnessId;
33806
+ row.dataset.importName = record8.name;
33807
+ row.setAttribute("aria-label", shown(record8.source.label));
33808
+ const agent = record8.source.harnessId;
33281
33809
  const mark = createMark(agent);
33282
33810
  const identity = document2.createElement("div");
33283
33811
  identity.className = "settings-pi-accounts__identity";
33284
33812
  const label = document2.createElement("strong");
33285
33813
  label.className = "settings-account-email";
33286
- label.textContent = shown(record7.source.label);
33814
+ label.textContent = shown(record8.source.label);
33287
33815
  label.title = label.textContent;
33288
33816
  label.translate = false;
33289
33817
  const meta3 = document2.createElement("div");
33290
33818
  meta3.className = "settings-account-metadata";
33291
- const source = sourceOf(record7);
33819
+ const source = sourceOf(record8);
33292
33820
  const parts = [
33293
33821
  metaPart(rendererAgentLabel(agent, pluginFor(agent)), "settings-pi-accounts__agent"),
33294
- metaPart(`${record7.name}/\u2026`, void 0, true),
33822
+ metaPart(`${record8.name}/\u2026`, void 0, true),
33295
33823
  metaPart(messages.copied)
33296
33824
  ];
33297
- const time3 = formatImportedAt(record7.importedAt);
33825
+ const time3 = formatImportedAt(record8.importedAt);
33298
33826
  if (time3) parts.push(metaPart(time3));
33299
33827
  parts.forEach((part, index) => {
33300
33828
  if (index > 0) {
@@ -33311,23 +33839,23 @@ ${detail}` : ""}`
33311
33839
  reimport.type = "button";
33312
33840
  reimport.className = "settings-account-action";
33313
33841
  reimport.textContent = messages.rowReimport;
33314
- reimport.setAttribute("aria-label", `${messages.reimport}: ${shown(record7.source.label)}`);
33842
+ reimport.setAttribute("aria-label", `${messages.reimport}: ${shown(record8.source.label)}`);
33315
33843
  reimport.disabled = busy || !source;
33316
33844
  reimport.title = messages.reimport;
33317
33845
  reimport.addEventListener("click", () => {
33318
- if (source) openDialog("reimport", { source, record: record7 }, `row:${record7.name}`);
33846
+ if (source) openDialog("reimport", { source, record: record8 }, `row:${record8.name}`);
33319
33847
  });
33320
33848
  const remove = document2.createElement("button");
33321
33849
  remove.type = "button";
33322
33850
  remove.className = "settings-account-action settings-pi-accounts__remove";
33323
33851
  remove.textContent = messages.rowRemove;
33324
- remove.setAttribute("aria-label", `${messages.remove}: ${shown(record7.source.label)}`);
33852
+ remove.setAttribute("aria-label", `${messages.remove}: ${shown(record8.source.label)}`);
33325
33853
  remove.disabled = busy;
33326
- remove.addEventListener("click", () => openDialog("remove", { record: record7 }, `row:${record7.name}`));
33854
+ remove.addEventListener("click", () => openDialog("remove", { record: record8 }, `row:${record8.name}`));
33327
33855
  if (source) actions.append(reimport);
33328
33856
  actions.append(remove);
33329
33857
  row.append(mark, identity, actions);
33330
- rows.set(record7.name, row);
33858
+ rows.set(record8.name, row);
33331
33859
  return row;
33332
33860
  };
33333
33861
  const loginTypeLabel = (type) => type === "oauth" ? messages.typeOauth : type === "api_key" ? messages.typeApiKey : messages.typeOther;
@@ -33480,7 +34008,7 @@ ${detail}` : ""}`
33480
34008
  const label = document2.createElement("strong");
33481
34009
  label.textContent = shown(choice.label);
33482
34010
  label.translate = false;
33483
- const existing = importedRecords().find((record7) => record7.source.id === choice.id);
34011
+ const existing = importedRecords().find((record8) => record8.source.id === choice.id);
33484
34012
  const agent = choice.harnessId;
33485
34013
  const detail = document2.createElement("span");
33486
34014
  detail.textContent = [
@@ -33521,7 +34049,7 @@ ${detail}` : ""}`
33521
34049
  const name = request.action === "list" ? void 0 : request.name;
33522
34050
  state = {
33523
34051
  source: state.source,
33524
- record: importedRecords().find((record7) => record7.name === name) ?? state.record
34052
+ record: importedRecords().find((record8) => record8.name === name) ?? state.record
33525
34053
  };
33526
34054
  show(next);
33527
34055
  } else {
@@ -33535,12 +34063,12 @@ ${detail}` : ""}`
33535
34063
  sourceRadios = [];
33536
34064
  body.replaceChildren();
33537
34065
  controls.replaceChildren();
33538
- const { source, record: record7 } = state;
34066
+ const { source, record: record8 } = state;
33539
34067
  const close = () => modal.close();
33540
34068
  if (mode === "add" || mode === "reimport") {
33541
34069
  title.textContent = mode === "add" ? messages.add : messages.reimport;
33542
34070
  modal.setAttribute("aria-label", title.textContent);
33543
- const active = source ?? record7?.source;
34071
+ const active = source ?? record8?.source;
33544
34072
  const choices = mode === "add" && state.choose ? compatibleSources() : [];
33545
34073
  if (choices.length > 1) body.append(sourcePicker(choices));
33546
34074
  else if (active) body.append(line(messages.source, shown(active.label)));
@@ -33549,8 +34077,8 @@ ${detail}` : ""}`
33549
34077
  input.maxLength = 48;
33550
34078
  input.autocomplete = "off";
33551
34079
  input.spellcheck = false;
33552
- input.value = record7?.name ?? (source ? defaultImportName(source, importedRecords()) : "codex");
33553
- input.readOnly = Boolean(record7);
34080
+ input.value = record8?.name ?? (source ? defaultImportName(source, importedRecords()) : "codex");
34081
+ input.readOnly = Boolean(record8);
33554
34082
  input.addEventListener("input", () => {
33555
34083
  nameEdited = true;
33556
34084
  });
@@ -33584,7 +34112,7 @@ ${detail}` : ""}`
33584
34112
  {
33585
34113
  action: mode === "reimport" ? "reimport" : "import",
33586
34114
  sourceId: source2.id,
33587
- name: record7?.name ?? input.value,
34115
+ name: record8?.name ?? input.value,
33588
34116
  confirmed: true
33589
34117
  },
33590
34118
  "done"
@@ -33594,16 +34122,16 @@ ${detail}` : ""}`
33594
34122
  )
33595
34123
  );
33596
34124
  (mode === "add" ? input : controls.children[0]).focus();
33597
- } else if (mode === "remove" && record7) {
34125
+ } else if (mode === "remove" && record8) {
33598
34126
  title.textContent = messages.remove;
33599
34127
  modal.setAttribute("aria-label", title.textContent);
33600
- body.append(note(messages.removeWarning.replace("{name}", record7.name)));
34128
+ body.append(note(messages.removeWarning.replace("{name}", record8.name)));
33601
34129
  const cancel = command(messages.cancel, close);
33602
34130
  controls.append(
33603
34131
  cancel,
33604
34132
  command(
33605
34133
  messages.remove,
33606
- () => submit({ action: "remove", name: record7.name, confirmed: true }, null),
34134
+ () => submit({ action: "remove", name: record8.name, confirmed: true }, null),
33607
34135
  "danger"
33608
34136
  )
33609
34137
  );
@@ -33611,7 +34139,7 @@ ${detail}` : ""}`
33611
34139
  } else {
33612
34140
  title.textContent = messages.doneTitle;
33613
34141
  modal.setAttribute("aria-label", title.textContent);
33614
- const name = record7?.name;
34142
+ const name = record8?.name;
33615
34143
  if (name) body.append(line(messages.entry, `${name}/\u2026`, true));
33616
34144
  body.append(note(messages.done));
33617
34145
  const done = command(messages.close, close, "primary");
@@ -33658,21 +34186,21 @@ ${detail}` : ""}`
33658
34186
  (candidate) => candidate.harnessId === TARGET_HARNESS_ID && source && candidate.providers.includes(source.provider)
33659
34187
  );
33660
34188
  if (!source || !target) return null;
33661
- const record7 = target.imports.find((candidate) => candidate.source.id === source.id);
34189
+ const record8 = target.imports.find((candidate) => candidate.source.id === source.id);
33662
34190
  const key = `${harnessId}:${accountLabel}:import`;
33663
34191
  const button2 = document2.createElement("button");
33664
34192
  button2.type = "button";
33665
34193
  button2.className = "settings-account-pi-import";
33666
34194
  button2.dataset.accountFocus = key;
33667
34195
  button2.disabled = busy;
33668
- const hint = record7 ? messages.importedHint.replace("{name}", record7.name) : messages.add;
34196
+ const hint = record8 ? messages.importedHint.replace("{name}", record8.name) : messages.add;
33669
34197
  button2.title = hint;
33670
34198
  button2.setAttribute("aria-label", hint);
33671
34199
  button2.append(createRendererAgentIcon("pi", 12, document2, pluginFor("pi")));
33672
- if (record7) {
34200
+ if (record8) {
33673
34201
  button2.dataset.state = "imported";
33674
34202
  const name = document2.createElement("code");
33675
- name.textContent = `${record7.name}/\u2026`;
34203
+ name.textContent = `${record8.name}/\u2026`;
33676
34204
  name.translate = false;
33677
34205
  button2.append(name, createRendererSettingsIcon("check", 12));
33678
34206
  } else {
@@ -33680,7 +34208,7 @@ ${detail}` : ""}`
33680
34208
  button2.append(messages.add);
33681
34209
  }
33682
34210
  button2.addEventListener("click", () => {
33683
- if (!record7) {
34211
+ if (!record8) {
33684
34212
  openDialog("add", { source }, key);
33685
34213
  return;
33686
34214
  }
@@ -33689,7 +34217,7 @@ ${detail}` : ""}`
33689
34217
  renderSection();
33690
34218
  sectionToggle.setAttribute("aria-expanded", "true");
33691
34219
  }
33692
- const row = rows.get(record7.name);
34220
+ const row = rows.get(record8.name);
33693
34221
  row?.scrollIntoView({ block: "nearest", behavior: "smooth" });
33694
34222
  row?.focus();
33695
34223
  });
@@ -35234,7 +35762,7 @@ ${detail}` : ""}`
35234
35762
  }
35235
35763
  });
35236
35764
  }
35237
- function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getLoadedSessionsClient = () => null, getRemoteConnections = () => null, requestUsageStatistics = () => Promise.reject(new Error("Local Host connection is unavailable")), requestVoiceSettings = () => Promise.reject(new Error("Local Host connection is unavailable"))) {
35765
+ function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getLoadedSessionsClient = () => null, getRemoteConnections = () => null, requestUsageStatistics = () => Promise.reject(new Error("Local Host connection is unavailable")), usageStatisticsHosts, requestVoiceSettings = () => Promise.reject(new Error("Local Host connection is unavailable"))) {
35238
35766
  return Object.freeze([
35239
35767
  createConnectionsSettingsPage(messages, getDiagnostics, void 0, getRemoteConnections),
35240
35768
  createRemoteConnectionsPage(messages, getRemoteConnections),
@@ -35248,7 +35776,8 @@ ${detail}` : ""}`
35248
35776
  createUsageStatisticsPage(
35249
35777
  consoleMessages(messages.locale),
35250
35778
  requestUsageStatistics,
35251
- messages.locale
35779
+ messages.locale,
35780
+ usageStatisticsHosts
35252
35781
  ),
35253
35782
  createAppearanceSettingsPage(messages, getLoadedSessionsClient),
35254
35783
  createVoiceSettingsPage(messages.locale, requestVoiceSettings, { desktop: true }),
@@ -35256,7 +35785,7 @@ ${detail}` : ""}`
35256
35785
  aboutPage(messages, getUpdateClient)
35257
35786
  ]);
35258
35787
  }
35259
- function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread, requestUsageStatistics) {
35788
+ function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread, requestUsageStatistics, usageStatisticsHosts) {
35260
35789
  return createRendererSettingsPageRegistry(
35261
35790
  createDefaultRendererSettingsPages(
35262
35791
  messages,
@@ -35267,7 +35796,8 @@ ${detail}` : ""}`
35267
35796
  openImportedThread,
35268
35797
  void 0,
35269
35798
  void 0,
35270
- requestUsageStatistics
35799
+ requestUsageStatistics,
35800
+ usageStatisticsHosts
35271
35801
  )
35272
35802
  );
35273
35803
  }
@@ -36041,6 +36571,20 @@ ${detail}` : ""}`
36041
36571
  gap: 18px;
36042
36572
  }
36043
36573
 
36574
+ .settings-connections-content {
36575
+ container: settings-connections / inline-size;
36576
+ }
36577
+
36578
+ @container settings-connections (max-width: 780px) {
36579
+ .settings-connections-layout {
36580
+ grid-template-columns: minmax(0, 1fr);
36581
+ }
36582
+
36583
+ .settings-connection-inspector {
36584
+ position: static;
36585
+ }
36586
+ }
36587
+
36044
36588
  .settings-connection-list,
36045
36589
  .settings-connection-inspector {
36046
36590
  overflow: hidden;
@@ -36049,14 +36593,36 @@ ${detail}` : ""}`
36049
36593
  border-radius: 10px;
36050
36594
  }
36051
36595
 
36596
+ .settings-connection-host-header {
36597
+ display: flex;
36598
+ align-items: center;
36599
+ min-width: 0;
36600
+ border-bottom: 1px solid var(--settings-divider);
36601
+ }
36602
+
36603
+ .settings-connection-update-all {
36604
+ flex: none;
36605
+ margin-right: 10px;
36606
+ white-space: nowrap;
36607
+ }
36608
+
36609
+ .settings-connection-update-result {
36610
+ margin: 0;
36611
+ padding: 7px 10px;
36612
+ color: var(--settings-muted);
36613
+ font-size: 11px;
36614
+ line-height: 18px;
36615
+ border-bottom: 1px solid var(--settings-divider);
36616
+ }
36617
+
36052
36618
  .settings-connection-host-strip {
36053
36619
  display: grid;
36620
+ flex: 1;
36054
36621
  grid-template-columns: 30px minmax(0, 1fr) 30px;
36055
36622
  align-items: center;
36056
36623
  min-width: 0;
36057
36624
  gap: 5px;
36058
36625
  padding: 9px 10px;
36059
- border-bottom: 1px solid var(--settings-divider);
36060
36626
  }
36061
36627
 
36062
36628
  .settings-connection-host-scroll {
@@ -36153,7 +36719,7 @@ ${detail}` : ""}`
36153
36719
  .settings-connection-table-header,
36154
36720
  .settings-connection-row {
36155
36721
  display: grid;
36156
- grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 108px;
36722
+ grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) minmax(108px, max-content);
36157
36723
  align-items: center;
36158
36724
  min-width: 0;
36159
36725
  gap: 10px;
@@ -36246,15 +36812,12 @@ ${detail}` : ""}`
36246
36812
  line-height: 18px;
36247
36813
  }
36248
36814
 
36249
- .settings-connection-row__status:has(.settings-connection-version-available) {
36250
- flex-wrap: wrap;
36251
- max-width: 160px;
36252
- }
36253
-
36254
36815
  .settings-connection-version-available {
36255
- flex-basis: 100%;
36256
- color: var(--settings-focus);
36257
- overflow-wrap: anywhere;
36816
+ flex: none;
36817
+ color: var(--settings-muted);
36818
+ font-size: 10px;
36819
+ line-height: 16px;
36820
+ white-space: nowrap;
36258
36821
  }
36259
36822
 
36260
36823
  .settings-connection-row__status::before {
@@ -36293,6 +36856,7 @@ ${detail}` : ""}`
36293
36856
  justify-content: flex-end;
36294
36857
  min-width: 0;
36295
36858
  gap: 4px;
36859
+ white-space: nowrap;
36296
36860
  }
36297
36861
 
36298
36862
  .settings-connection-row__handle {
@@ -40291,7 +40855,7 @@ th[aria-sort] .console-usage-sort__mark {
40291
40855
  place();
40292
40856
  const observer = ownerWindow ? new ownerWindow.MutationObserver((records) => {
40293
40857
  const titlebar = source?.closest("[data-app-shell-titlebar]");
40294
- if (!source?.isConnected || records.some((record7) => titlebar?.contains(record7.target))) {
40858
+ if (!source?.isConnected || records.some((record8) => titlebar?.contains(record8.target))) {
40295
40859
  place();
40296
40860
  }
40297
40861
  }) : null;
@@ -40726,6 +41290,7 @@ ${usage_default}`;
40726
41290
  options.getLoadedSessionsClient ?? (() => null),
40727
41291
  options.getRemoteConnections ?? (() => null),
40728
41292
  options.requestUsageStatistics,
41293
+ options.usageStatisticsHosts,
40729
41294
  options.requestVoiceSettings
40730
41295
  );
40731
41296
  const nextShell = installRendererSettingsShell(definitions, messages, ownerWindow.document, {
@@ -40875,6 +41440,30 @@ ${usage_default}`;
40875
41440
  };
40876
41441
  }
40877
41442
 
41443
+ // src/renderer-usage-statistics-source.ts
41444
+ function createRendererUsageStatisticsSource(options) {
41445
+ return {
41446
+ async request(method, params, hostId2) {
41447
+ const route = options.routing()?.forHost(hostId2);
41448
+ if (options.disposed() || !route) {
41449
+ throw new Error(`Host connection is unavailable (${hostId2})`);
41450
+ }
41451
+ return route.manager.sendRequest(method, params);
41452
+ },
41453
+ async hosts() {
41454
+ const connections = await options.listConnections().catch(() => []);
41455
+ const names = new Map(
41456
+ connections.map((connection) => [connection.hostId, connection.displayName])
41457
+ );
41458
+ const known = options.routing()?.knownHostIds?.() ?? [];
41459
+ return [.../* @__PURE__ */ new Set([...known, ...names.keys()])].map((hostId2) => {
41460
+ const label = names.get(hostId2);
41461
+ return label === void 0 ? { hostId: hostId2 } : { hostId: hostId2, label };
41462
+ });
41463
+ }
41464
+ };
41465
+ }
41466
+
40878
41467
  // src/renderer-native-composer-controller.ts
40879
41468
  var MAX_ELEMENT_DEPTH = 4;
40880
41469
  var MAX_FIBER_DEPTH = 40;
@@ -41809,6 +42398,11 @@ ${usage_default}`;
41809
42398
  return route.manager.sendRequest(method, params);
41810
42399
  };
41811
42400
  const stopVoiceSync = startVoiceSettingsSync(window, requestLocalSettings);
42401
+ const usageStatistics = createRendererUsageStatisticsSource({
42402
+ routing: () => window.__codexhostHostRoutingV1,
42403
+ listConnections: () => remoteConnections.ssh.list(),
42404
+ disposed: () => disposed
42405
+ });
41812
42406
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
41813
42407
  requestVoiceSettings: (params) => requestLocalSettings("codexhost/voice/settings", params),
41814
42408
  getRemoteConnections: () => remoteConnections,
@@ -41816,11 +42410,8 @@ ${usage_default}`;
41816
42410
  getAccountClient: () => modelControl,
41817
42411
  getConnectionDiagnostics: () => connectionDiagnostics,
41818
42412
  getLoadedSessionsClient: () => modelClientForHost("local"),
41819
- async requestUsageStatistics(method, params) {
41820
- const route = window.__codexhostHostRoutingV1?.forHost("local");
41821
- if (disposed || !route) throw new Error("Local Host connection is unavailable");
41822
- return route.manager.sendRequest(method, params);
41823
- },
42413
+ requestUsageStatistics: usageStatistics.request,
42414
+ usageStatisticsHosts: usageStatistics.hosts,
41824
42415
  getSessionImportClient: () => {
41825
42416
  const client = modelClientForHost("local");
41826
42417
  const sources = client?.listSessionImportSources;
@@ -41917,9 +42508,10 @@ ${usage_default}`;
41917
42508
  };
41918
42509
  const notifySubmission = (composer, trigger) => {
41919
42510
  const state = controller.recordSubmission(composer);
42511
+ const createsThread = state.phase === "draft";
41920
42512
  const preferenceStorage = rendererNewThreadPreferenceStorage(activeModelHostId(composer));
41921
- writeNewThreadAgentPreference(state.agent, preferenceStorage);
41922
- if (state.agent !== "codex") {
42513
+ if (createsThread) writeNewThreadAgentPreference(state.agent, preferenceStorage);
42514
+ if (createsThread && state.agent !== "codex") {
41923
42515
  const model2 = controller.modelForAgent(composer, state.agent);
41924
42516
  if (model2) {
41925
42517
  writeNewThreadExternalConfigurationPreference(
@@ -44062,8 +44654,8 @@ ${usage_default}`;
44062
44654
  // src/renderer-voice-capability.ts
44063
44655
  var GLOBAL = "__codexhostVoiceCapabilityV1";
44064
44656
  var INFERENCE_GATE = "3683713372";
44065
- var record6 = (value) => value !== null && typeof value === "object";
44066
- var capable = (value) => record6(value) && typeof value.isLoading === "boolean" && typeof value.isError === "boolean" && typeof value.isCapable === "boolean";
44657
+ var record7 = (value) => value !== null && typeof value === "object";
44658
+ var capable = (value) => record7(value) && typeof value.isLoading === "boolean" && typeof value.isError === "boolean" && typeof value.isCapable === "boolean";
44067
44659
  var equal = (a, b) => a.isLoading === b.isLoading && a.isError === b.isError && a.isCapable === b.isCapable;
44068
44660
  function replayVoiceCapability(store, atom, gate) {
44069
44661
  const evaluate = (override) => {
@@ -44104,7 +44696,7 @@ ${usage_default}`;
44104
44696
  const result = [];
44105
44697
  const seen = /* @__PURE__ */ new Set();
44106
44698
  let hook = fiber.memoizedState;
44107
- while (record6(hook) && result.length < 4096 && !seen.has(hook)) {
44699
+ while (record7(hook) && result.length < 4096 && !seen.has(hook)) {
44108
44700
  seen.add(hook);
44109
44701
  result.push(hook);
44110
44702
  hook = hook.next;
@@ -44120,7 +44712,7 @@ ${usage_default}`;
44120
44712
  let root = ancestors[0];
44121
44713
  if (chat) {
44122
44714
  const owners = ancestors.filter(
44123
- ({ memoizedProps: props }) => record6(props) && props.isWorkConversation === false && props.isTemporaryChat === false && props.shouldBlockExternalEgress === false && props.readOnly !== true && typeof props.conversationId === "string" && record6(props.composerSelectionContext) && props.composerSelectionContext.conversationId === props.conversationId
44715
+ ({ memoizedProps: props }) => record7(props) && props.isWorkConversation === false && props.isTemporaryChat === false && props.shouldBlockExternalEgress === false && props.readOnly !== true && typeof props.conversationId === "string" && record7(props.composerSelectionContext) && props.composerSelectionContext.conversationId === props.conversationId
44124
44716
  );
44125
44717
  if (owners.length !== 1) return fibers;
44126
44718
  root = owners[0];
@@ -44132,7 +44724,7 @@ ${usage_default}`;
44132
44724
  const ancestors2 = committedReactAncestors(Object.getOwnPropertyDescriptor(element, key)?.value);
44133
44725
  const boundary = ancestors2.indexOf(root);
44134
44726
  if (boundary < 0) continue;
44135
- for (const fiber of ancestors2.slice(0, boundary + 1)) if (record6(fiber)) fibers.add(fiber);
44727
+ for (const fiber of ancestors2.slice(0, boundary + 1)) if (record7(fiber)) fibers.add(fiber);
44136
44728
  }
44137
44729
  return fibers;
44138
44730
  }
@@ -44153,7 +44745,7 @@ ${usage_default}`;
44153
44745
  const list = hooks(fiber);
44154
44746
  for (const memo of list) {
44155
44747
  const [signal, deps] = Array.isArray(memo.memoizedState) ? memo.memoizedState : [];
44156
- if (!record6(signal) || !Array.isArray(deps) || deps.length !== 5 || deps[3] !== family || !(deps[1] instanceof Map) || deps[2] !== void 0)
44748
+ if (!record7(signal) || !Array.isArray(deps) || deps.length !== 5 || deps[3] !== family || !(deps[1] instanceof Map) || deps[2] !== void 0)
44157
44749
  continue;
44158
44750
  const chain = deps[1];
44159
44751
  const node = chain.get(family.scope.id);
@@ -44162,7 +44754,7 @@ ${usage_default}`;
44162
44754
  const matches = [...members].filter(([, member]) => member.cacheKey === deps[0]);
44163
44755
  if (matches.length !== 1 || typeof matches[0]?.[0] !== "string") continue;
44164
44756
  const parameter = JSON.parse(matches[0][0]);
44165
- if (!record6(parameter) || Object.keys(parameter).some((key) => key !== "name" && key !== "hostId") || parameter.name !== "voice.realtime" && parameter.name !== "voice.thread" || realtimeOnly && parameter.name !== "voice.realtime" || parameter.hostId !== void 0 && parameter.hostId !== "local" || parameter.name === "voice.thread" && parameter.hostId !== "local")
44757
+ if (!record7(parameter) || Object.keys(parameter).some((key) => key !== "name" && key !== "hostId") || parameter.name !== "voice.realtime" && parameter.name !== "voice.thread" || realtimeOnly && parameter.name !== "voice.realtime" || parameter.hostId !== void 0 && parameter.hostId !== "local" || parameter.name === "voice.thread" && parameter.hostId !== "local")
44166
44758
  continue;
44167
44759
  const subscriptions = list.filter((hook2) => {
44168
44760
  const state = hook2.memoizedState;
@@ -44175,7 +44767,7 @@ ${usage_default}`;
44175
44767
  const effect = hook.next?.next?.memoizedState;
44176
44768
  const store = signal.store;
44177
44769
  const gate = gates.resolve(node, chain, INFERENCE_GATE)?.atom;
44178
- if (!gate || !instance || !record6(effect) || !Array.isArray(effect.deps) || effect.deps[0] !== subscriber.subscribe || typeof effect.create !== "function" || typeof instance.getSnapshot !== "function" || !capable(store.get(signal.atom)) || !capable(subscriber.getSnapshot()) || !capable(instance.getSnapshot()) || !equal(
44770
+ if (!gate || !instance || !record7(effect) || !Array.isArray(effect.deps) || effect.deps[0] !== subscriber.subscribe || typeof effect.create !== "function" || typeof instance.getSnapshot !== "function" || !capable(store.get(signal.atom)) || !capable(subscriber.getSnapshot()) || !capable(instance.getSnapshot()) || !equal(
44179
44771
  store.get(signal.atom),
44180
44772
  subscriber.getSnapshot()
44181
44773
  ) || !equal(subscriber.getSnapshot(), instance.getSnapshot()) || subscriber.createRender?.() != null)
@@ -44269,7 +44861,7 @@ ${usage_default}`;
44269
44861
  target[GLOBAL] = handle;
44270
44862
  const hookActive = (key) => {
44271
44863
  const hook = target[key];
44272
- return record6(hook) && hook.status === "active";
44864
+ return record7(hook) && hook.status === "active";
44273
44865
  };
44274
44866
  const currentRoute = () => {
44275
44867
  try {