@codexhost/cli-darwin-x64 0.13.3 → 0.14.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.
@@ -178,9 +178,10 @@
178
178
  clearPendingSubmission(composer) {
179
179
  this.#pendingSubmissions.delete(this.#state(composer));
180
180
  }
181
+ /** Only a draft's submission creates a Thread, so only it sets the next new-Thread Agent. */
181
182
  recordSubmission(composer) {
182
183
  const state = this.#state(composer);
183
- this.#lastSubmittedAgent = state.agent;
184
+ if (state.phase === "draft") this.#lastSubmittedAgent = state.agent;
184
185
  return state;
185
186
  }
186
187
  transfer(source, replacement, target = null) {
@@ -14930,10 +14931,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14930
14931
  var project = external_exports.string().min(1).max(4096);
14931
14932
  var usageStatisticsParamsSchema = external_exports.strictObject({
14932
14933
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
14934
+ /** Display timezone for ranges, dates and hourly buckets; omitted means the Host's local zone. */
14935
+ timeZone: external_exports.string().min(1).max(128).refine((value) => {
14936
+ try {
14937
+ new Intl.DateTimeFormat("en", { timeZone: value });
14938
+ return true;
14939
+ } catch {
14940
+ return false;
14941
+ }
14942
+ }, "Unsupported usage statistics timezone").optional(),
14933
14943
  harness: harness.optional(),
14934
14944
  model: model.nullable().optional(),
14935
14945
  project: project.nullable().optional(),
14936
- /** One local date inside the range. */
14946
+ /** One date in the query timezone inside the range. */
14937
14947
  date: localDate.optional()
14938
14948
  });
14939
14949
  var usageStatisticsTotalsSchema = external_exports.strictObject({
@@ -14969,7 +14979,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14969
14979
  });
14970
14980
  var usageStatisticsResultSchema = external_exports.strictObject({
14971
14981
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
14972
- /** First local date of the range; null for all time. */
14982
+ /** First date in the query timezone (Host local when omitted); null for all time. */
14973
14983
  from: localDate.nullable(),
14974
14984
  to: localDate,
14975
14985
  /** The filters these figures apply, echoed so a late answer to older filters is recognised. */
@@ -15009,7 +15019,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15009
15019
  totals: usageStatisticsTotalsSchema,
15010
15020
  /** Each date and Harness of the range: every filter except the day. */
15011
15021
  daily: external_exports.array(external_exports.strictObject({ date: localDate, harness, ...totals })).max(2e5),
15012
- /** Local weekday (0 = Sunday) and hour, all filters applied. */
15022
+ /** Weekday (0 = Sunday) and hour in the query timezone, all filters applied. */
15013
15023
  hourly: external_exports.array(external_exports.strictObject({
15014
15024
  weekday: external_exports.number().int().min(0).max(6),
15015
15025
  hour: external_exports.number().int().min(0).max(23),
@@ -15070,6 +15080,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15070
15080
  "codexhost/harness/display-settings/set",
15071
15081
  "codexhost/harness/inspect",
15072
15082
  "codexhost/harness/installation",
15083
+ "codexhost/harness/installation/start",
15084
+ "codexhost/harness/installation/status",
15073
15085
  "codexhost/harness/launch-settings/get",
15074
15086
  "codexhost/harness/launch-settings/set",
15075
15087
  "codexhost/harness/web-ui/open",
@@ -15148,7 +15160,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15148
15160
  var nonNegativeSafeIntegerSchema = external_exports.number().int().safe().nonnegative();
15149
15161
  var finiteNonNegativeNumberSchema = external_exports.number().finite().nonnegative();
15150
15162
  var cacheHitRatePercentSchema = external_exports.number().finite().min(0).max(100);
15163
+ var indicatorTextSchema = (maximum) => harnessLocalizedTextSchema.refine((entries) => Object.values(entries).every((text) => text.length <= maximum));
15164
+ var composerIndicatorsSchema = external_exports.array(external_exports.object({
15165
+ kind: external_exports.literal("countdown"),
15166
+ id: external_exports.string().min(1).max(64),
15167
+ expiresAtUnix: nonNegativeSafeIntegerSchema,
15168
+ label: indicatorTextSchema(256).optional(),
15169
+ tooltip: indicatorTextSchema(1024).optional()
15170
+ }).strict()).max(2).refine((entries) => new Set(entries.map(({ id }) => id)).size === entries.length, {
15171
+ message: "Composer indicator IDs must be unique"
15172
+ });
15151
15173
  var threadUsageSnapshotSchema = external_exports.object({
15174
+ composerIndicators: composerIndicatorsSchema.optional(),
15152
15175
  inputTokens: nonNegativeSafeIntegerSchema.optional(),
15153
15176
  cachedInputTokens: nonNegativeSafeIntegerSchema.optional(),
15154
15177
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
@@ -15564,7 +15587,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15564
15587
  }).strict().optional(),
15565
15588
  autonomousTurns: external_exports.object({
15566
15589
  observe: external_exports.boolean()
15567
- }).strict().optional()
15590
+ }).strict().optional(),
15591
+ /** True only when `turn.steer` delivers input into the active Turn through a native primitive. */
15592
+ steer: external_exports.boolean().optional()
15568
15593
  }).strict();
15569
15594
  var harnessConfigurationStateSchema = external_exports.object({
15570
15595
  effectiveModel: harnessModelRefSchema.optional(),
@@ -15612,7 +15637,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15612
15637
  var harnessInspectParamsSchema = external_exports.object({
15613
15638
  harnessId: harnessIdSchema,
15614
15639
  cwd: nonBlankTextSchema2.max(16384).optional(),
15615
- refresh: external_exports.boolean().optional()
15640
+ refresh: external_exports.boolean().optional(),
15641
+ /** `availability` asks only whether the Harness can run; see `InspectHarnessInput.purpose`. */
15642
+ purpose: external_exports.literal("availability").optional()
15616
15643
  }).strict();
15617
15644
  var harnessWebUiOpenParamsSchema = external_exports.object({
15618
15645
  harnessId: harnessIdSchema
@@ -15695,6 +15722,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15695
15722
  seen.add(thread.threadId);
15696
15723
  }
15697
15724
  });
15725
+ var threadSteeringInspectParamsSchema = external_exports.object({
15726
+ threadId: hostThreadIdSchema,
15727
+ /** The text about to be steered; a Harness command always stops and restarts. */
15728
+ text: external_exports.string().optional()
15729
+ }).strict();
15730
+ var THREAD_STEER_DELIVERY_PARAM = "codexhostSteerDelivery";
15731
+ var threadSteerDeliverySchema = external_exports.enum(["activeTurn", "newTurn"]);
15732
+ var threadSteeringInspectResultSchema = external_exports.object({
15733
+ delivery: external_exports.enum(["official", "activeTurn", "newTurn"])
15734
+ }).strict();
15698
15735
 
15699
15736
  // ../shared-contracts/dist/harness-plugins.js
15700
15737
  var HARNESS_PLUGIN_MANIFEST_MAX_BYTES = 32 * 1024;
@@ -15813,7 +15850,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15813
15850
  }).strict();
15814
15851
 
15815
15852
  // ../shared-contracts/dist/harness-installation.js
15816
- var HARNESS_INSTALLATION_METHOD = "codexhost/harness/installation";
15853
+ var HARNESS_INSTALLATION_START_METHOD = "codexhost/harness/installation/start";
15854
+ var HARNESS_INSTALLATION_STATUS_METHOD = "codexhost/harness/installation/status";
15817
15855
  var harnessInstallationParamsSchema = external_exports.object({
15818
15856
  harnessId: harnessIdSchema,
15819
15857
  action: external_exports.enum(["check", "update", "install"])
@@ -15823,6 +15861,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15823
15861
  latestVersion: external_exports.string().min(1).max(128),
15824
15862
  updateAvailable: external_exports.boolean(),
15825
15863
  canUpdate: external_exports.boolean(),
15864
+ /** Defaults to CLI; some Harnesses ship only inside a desktop application. */
15865
+ versionKind: external_exports.enum(["cli", "application"]).optional(),
15866
+ /** Official download opened by the user; does not imply an automatic installation. */
15867
+ manualUpdateUrl: external_exports.url().max(2048).refine((value) => /^https:\/\/[^/?#@]+(?:[/?#]|$)/iu.test(value), "Manual updates require a credential-free HTTPS URL").optional(),
15826
15868
  // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
15827
15869
  message: external_exports.string().max(2048).optional(),
15828
15870
  messageCode: external_exports.string().min(1).max(128).optional(),
@@ -15830,6 +15872,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15830
15872
  notice: harnessLocalizedTextSchema.optional(),
15831
15873
  latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
15832
15874
  }).strict();
15875
+ var harnessInstallationTaskParamsSchema = external_exports.object({ taskId: external_exports.string().uuid() }).strict();
15876
+ var harnessInstallationTaskSchema = external_exports.discriminatedUnion("status", [
15877
+ external_exports.object({ taskId: external_exports.string().uuid(), status: external_exports.literal("running") }).strict(),
15878
+ external_exports.object({
15879
+ taskId: external_exports.string().uuid(),
15880
+ status: external_exports.literal("succeeded"),
15881
+ result: harnessInstallationStateSchema
15882
+ }).strict(),
15883
+ external_exports.object({
15884
+ taskId: external_exports.string().uuid(),
15885
+ status: external_exports.literal("failed"),
15886
+ error: external_exports.object({ code: external_exports.number().int(), message: external_exports.string().min(1).max(2048) }).strict()
15887
+ }).strict()
15888
+ ]);
15833
15889
 
15834
15890
  // ../shared-contracts/dist/codex-accounts.js
15835
15891
  var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
@@ -16240,9 +16296,63 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16240
16296
  // ../shared-contracts/dist/index.js
16241
16297
  var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
16242
16298
 
16243
- // src/codex-ssh-adapter.ts
16299
+ // src/codex-native-services.ts
16300
+ async function loadCodexNativeServices(ownerWindow, matchesServices, importModule = (url2) => import(url2)) {
16301
+ if (!ownerWindow.document || !ownerWindow.location) return null;
16302
+ const page = new URL(ownerWindow.location.href);
16303
+ const urls = /* @__PURE__ */ new Set();
16304
+ for (const link of ownerWindow.document.querySelectorAll('link[rel="modulepreload"][href]')) {
16305
+ const url2 = new URL(link.getAttribute("href") ?? "", page);
16306
+ if (url2.protocol === page.protocol && url2.host === page.host && url2.pathname.endsWith(".js") && !url2.search && !url2.hash)
16307
+ urls.add(url2.href);
16308
+ }
16309
+ const candidates = [...urls].sort(
16310
+ (a, b) => Number(b.includes("/app-shared-")) - Number(a.includes("/app-shared-"))
16311
+ );
16312
+ for (const url2 of candidates) {
16313
+ let module;
16314
+ try {
16315
+ module = await importModule(url2);
16316
+ } catch {
16317
+ continue;
16318
+ }
16319
+ const matches = new Set(Object.values(module).filter(matchesServices));
16320
+ if (matches.size > 1) throw new Error("Codex native services are unavailable in this window");
16321
+ if (matches.size === 1) return [...matches][0] ?? null;
16322
+ }
16323
+ return null;
16324
+ }
16325
+
16326
+ // src/codex-ssh-services.ts
16244
16327
  var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16328
+ function isSshServices(value) {
16329
+ if (!record2(value) || !Object.hasOwn(value, "sshConnections") || !Object.hasOwn(value, "remoteConnections"))
16330
+ return false;
16331
+ const ssh = value.sshConnections;
16332
+ const remote = value.remoteConnections;
16333
+ return ssh != null && remote != null && typeof ssh.refresh === "function" && typeof ssh.save === "function" && typeof remote.getState === "function" && typeof remote.setAutoConnect === "function";
16334
+ }
16335
+ async function loadCodexSshServices(ownerWindow, importModule) {
16336
+ return loadCodexNativeServices(ownerWindow, isSshServices, importModule);
16337
+ }
16338
+
16339
+ // src/codex-ssh-adapter.ts
16340
+ var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16245
16341
  function createCodexSshClient(ownerWindow) {
16342
+ let nativeServices;
16343
+ function services() {
16344
+ nativeServices ??= loadCodexSshServices(ownerWindow).then(
16345
+ (value) => {
16346
+ if (!value) nativeServices = void 0;
16347
+ return value;
16348
+ },
16349
+ (error51) => {
16350
+ nativeServices = void 0;
16351
+ throw error51;
16352
+ }
16353
+ );
16354
+ return nativeServices;
16355
+ }
16246
16356
  function request(operation, params, signal) {
16247
16357
  const bridge = ownerWindow.electronBridge;
16248
16358
  if (!bridge?.sendMessageFromView)
@@ -16250,7 +16360,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16250
16360
  if (signal?.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
16251
16361
  return new Promise((resolve, reject) => {
16252
16362
  const requestId = ownerWindow.crypto.randomUUID();
16363
+ let settled = false;
16253
16364
  const finish = (error51, value) => {
16365
+ if (settled) return;
16366
+ settled = true;
16254
16367
  ownerWindow.clearTimeout(timer);
16255
16368
  ownerWindow.removeEventListener("message", receive);
16256
16369
  signal?.removeEventListener("abort", abort);
@@ -16260,10 +16373,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16260
16373
  const abort = () => finish(new DOMException("Aborted", "AbortError"));
16261
16374
  const receive = (event) => {
16262
16375
  const message2 = event.data;
16263
- if (!record2(message2) || message2.type !== "fetch-response" || message2.requestId !== requestId)
16376
+ if (!record3(message2) || message2.type !== "fetch-response" || message2.requestId !== requestId)
16264
16377
  return;
16265
16378
  if (message2.responseType !== "success" || typeof message2.status !== "number" || message2.status < 200 || message2.status >= 300 || typeof message2.bodyJsonString !== "string") {
16266
- finish(new Error("Codex SSH request failed; check the native Connections settings"));
16379
+ const detail = typeof message2.error === "string" ? `: ${message2.error}` : "";
16380
+ finish(
16381
+ new Error(`Codex SSH request failed; check the native Connections settings${detail}`)
16382
+ );
16267
16383
  return;
16268
16384
  }
16269
16385
  try {
@@ -16278,30 +16394,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16278
16394
  );
16279
16395
  ownerWindow.addEventListener("message", receive);
16280
16396
  signal?.addEventListener("abort", abort, { once: true });
16281
- try {
16282
- Promise.resolve(
16283
- bridge.sendMessageFromView({
16397
+ void (async () => {
16398
+ const native = await services();
16399
+ if (settled) return;
16400
+ if (native) {
16401
+ let result;
16402
+ switch (operation) {
16403
+ case "refresh-remote-connections":
16404
+ result = await native.sshConnections.refresh();
16405
+ break;
16406
+ case "save-codex-managed-remote-ssh-connections":
16407
+ result = await native.sshConnections.save(params);
16408
+ break;
16409
+ case "set-remote-connection-auto-connect":
16410
+ result = await native.remoteConnections.setAutoConnect(params);
16411
+ break;
16412
+ case "app-server-connection-state":
16413
+ result = await native.remoteConnections.getState(String(params.hostId));
16414
+ break;
16415
+ }
16416
+ finish(null, result);
16417
+ } else {
16418
+ await bridge.sendMessageFromView({
16284
16419
  type: "fetch",
16285
16420
  requestId,
16286
16421
  method: "POST",
16287
16422
  url: `vscode://codex/${operation}`,
16288
16423
  body: JSON.stringify(params),
16289
16424
  reportUploadProgress: false
16290
- })
16291
- ).catch((error51) => finish(error51));
16292
- } catch (error51) {
16293
- finish(error51);
16294
- }
16425
+ });
16426
+ }
16427
+ })().catch((error51) => finish(error51));
16295
16428
  });
16296
16429
  }
16297
16430
  async function list(signal) {
16298
16431
  const result = await request("refresh-remote-connections", {}, signal);
16299
- if (!record2(result) || !Array.isArray(result.remoteConnections))
16432
+ if (!record3(result) || !Array.isArray(result.remoteConnections))
16300
16433
  throw new Error("Invalid Codex SSH connection list");
16301
16434
  return result.remoteConnections.filter(
16302
- (item) => !(record2(item) && ["wsl", "remote-control"].includes(String(item.source)))
16435
+ (item) => !(record3(item) && ["wsl", "remote-control"].includes(String(item.source)))
16303
16436
  ).map((item) => {
16304
- if (!record2(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)) {
16437
+ 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)) {
16305
16438
  throw new Error(
16306
16439
  "This Codex version uses an unsupported SSH configuration; open native settings"
16307
16440
  );
@@ -16373,11 +16506,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16373
16506
  },
16374
16507
  async state(hostId2, signal) {
16375
16508
  const result = await request("app-server-connection-state", { hostId: hostId2 }, signal);
16376
- if (!record2(result) || typeof result.state !== "string")
16509
+ if (!record3(result) || typeof result.state !== "string")
16377
16510
  throw new Error("Invalid Codex connection state");
16378
16511
  return {
16379
16512
  state: result.state,
16380
- error: typeof result.error === "string" ? result.error : record2(result.error) && typeof result.error.message === "string" ? result.error.message : null
16513
+ error: typeof result.error === "string" ? result.error : record3(result.error) && typeof result.error.message === "string" ? result.error.message : null
16381
16514
  };
16382
16515
  }
16383
16516
  };
@@ -17553,7 +17686,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17553
17686
  }
17554
17687
 
17555
17688
  // src/settings/harness-version-panel.ts
17556
- function createHarnessVersionPanel(document2, messages, signal, agent, client) {
17689
+ function createHarnessVersionPanel(document2, messages, signal, agent, client, onChange = () => void 0) {
17557
17690
  const panel = document2.createElement("section");
17558
17691
  panel.className = "settings-harness-version";
17559
17692
  panel.dataset.harnessVersion = agent;
@@ -17579,37 +17712,67 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17579
17712
  panel.append(heading, versions, status, actions, note);
17580
17713
  let state;
17581
17714
  let busy = false;
17715
+ let updating = false;
17582
17716
  let unsupported = false;
17717
+ let updatePromise = null;
17583
17718
  const render = () => {
17719
+ heading.textContent = state?.versionKind === "application" ? messages.harnessVersionApplicationTitle : messages.harnessVersionTitle;
17584
17720
  current.textContent = `${messages.harnessVersionCurrent}: ${state?.currentVersion ?? "\u2014"}`;
17585
17721
  latest.textContent = `${messages.harnessVersionLatest}: ${harnessVersionLabel(state, messages.harnessVersion)}`;
17586
17722
  update.disabled = busy || signal.aborted || !state?.canUpdate || !state.updateAvailable;
17723
+ const manualUrl = !state?.canUpdate && state?.updateAvailable ? state.manualUpdateUrl : void 0;
17724
+ actions.replaceChildren(update);
17725
+ update.hidden = !busy && !signal.aborted && Boolean(manualUrl);
17726
+ if (update.hidden && manualUrl) {
17727
+ const download2 = document2.createElement("a");
17728
+ download2.className = "settings-command-button settings-command-button--secondary";
17729
+ download2.dataset.harnessVersionAction = "download-update";
17730
+ download2.textContent = messages.harnessVersionDownloadUpdate;
17731
+ download2.href = manualUrl;
17732
+ download2.target = "_blank";
17733
+ download2.rel = "noopener noreferrer";
17734
+ actions.append(download2);
17735
+ }
17587
17736
  update.textContent = state?.canUpdate && state.latestVersion !== "Unknown" && !state.updateAvailable ? messages.harnessVersionUpToDate : messages.harnessVersionUpdate;
17588
17737
  const detail = harnessVersionNote(state, messages.harnessVersion);
17589
- note.textContent = detail ? `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
17738
+ note.textContent = detail ? !state?.canUpdate ? detail : `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
17590
17739
  };
17591
17740
  const run = async (action) => {
17592
- if (busy || unsupported || signal.aborted) return;
17593
- if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return;
17741
+ if (busy || unsupported || signal.aborted) return false;
17742
+ if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return false;
17594
17743
  busy = true;
17744
+ updating = action === "update";
17745
+ onChange();
17595
17746
  status.textContent = action === "check" ? messages.harnessVersionChecking : messages.harnessVersionUpdating;
17596
17747
  render();
17597
17748
  try {
17598
17749
  const result = await client.run(action);
17599
- if (signal.aborted) return;
17750
+ if (signal.aborted) return false;
17600
17751
  state = result;
17601
17752
  status.textContent = !result.canUpdate ? messages.harnessVersionManual : action === "update" ? result.updateAvailable ? messages.harnessVersionFailed : messages.harnessVersionUpdated : "";
17753
+ return result.canUpdate && !result.updateAvailable;
17602
17754
  } catch (error51) {
17603
- if (signal.aborted) return;
17755
+ if (signal.aborted) return false;
17604
17756
  unsupported = typeof error51 === "object" && error51 !== null && "code" in error51 && (error51.code === -32601 || error51.code === -32078);
17605
17757
  status.textContent = unsupported ? messages.harnessVersionUnsupported : messages.harnessVersionFailed;
17606
17758
  if (state) state = { ...state, canUpdate: false };
17759
+ return false;
17607
17760
  } finally {
17608
17761
  busy = false;
17609
- if (!signal.aborted) render();
17762
+ updating = false;
17763
+ if (!signal.aborted) {
17764
+ render();
17765
+ onChange();
17766
+ }
17610
17767
  }
17611
17768
  };
17612
- update.addEventListener("click", () => void run("update"));
17769
+ const updateVersion = () => {
17770
+ updatePromise ??= run("update").finally(() => {
17771
+ updatePromise = null;
17772
+ });
17773
+ return updatePromise;
17774
+ };
17775
+ update.addEventListener("click", () => void updateVersion());
17613
17776
  const owner = document2.defaultView;
17614
17777
  let checkFrame = null;
17615
17778
  let checkTimer = null;
@@ -17636,7 +17799,95 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17636
17799
  } else {
17637
17800
  void run("check");
17638
17801
  }
17639
- return panel;
17802
+ return {
17803
+ element: panel,
17804
+ get state() {
17805
+ return state;
17806
+ },
17807
+ get updating() {
17808
+ return updating;
17809
+ },
17810
+ update: updateVersion
17811
+ };
17812
+ }
17813
+
17814
+ // src/settings/connection-versions.ts
17815
+ function createConnectionVersions(document2, messages, signal, diagnostics, onChange) {
17816
+ const panels = /* @__PURE__ */ new Map();
17817
+ const installation = diagnostics?.installation?.bind(diagnostics);
17818
+ return {
17819
+ get(hostId2, agent) {
17820
+ if (!installation || signal.aborted) return void 0;
17821
+ const key = JSON.stringify([hostId2, agent]);
17822
+ let panel = panels.get(key);
17823
+ if (!panel) {
17824
+ panel = createHarnessVersionPanel(
17825
+ document2,
17826
+ messages,
17827
+ signal,
17828
+ agent,
17829
+ {
17830
+ run: (action) => installation(hostId2, agent, action)
17831
+ },
17832
+ onChange
17833
+ );
17834
+ panels.set(key, panel);
17835
+ }
17836
+ return panel;
17837
+ }
17838
+ };
17839
+ }
17840
+
17841
+ // src/settings/connection-updates.ts
17842
+ function createConnectionUpdates(document2, messages, signal, onChange) {
17843
+ const batches = /* @__PURE__ */ new Map();
17844
+ const eligible = (panels) => panels.filter((panel) => panel.state?.updateAvailable && panel.state.canUpdate);
17845
+ const start = async (hostId2, panels) => {
17846
+ if (signal.aborted || batches.get(hostId2)?.running) return;
17847
+ const targets = eligible(panels);
17848
+ if (!targets.length) return;
17849
+ const batch = { running: true, total: targets.length, succeeded: 0, failed: 0 };
17850
+ batches.set(hostId2, batch);
17851
+ onChange();
17852
+ await Promise.all(
17853
+ targets.map(async (panel) => {
17854
+ try {
17855
+ if (await panel.update()) batch.succeeded++;
17856
+ else batch.failed++;
17857
+ } catch {
17858
+ batch.failed++;
17859
+ }
17860
+ if (!signal.aborted) onChange();
17861
+ })
17862
+ );
17863
+ batch.running = false;
17864
+ if (!signal.aborted) onChange();
17865
+ };
17866
+ return {
17867
+ createControls(hostId2, panels) {
17868
+ const batch = batches.get(hostId2);
17869
+ const count3 = eligible(panels).length;
17870
+ const button2 = document2.createElement("button");
17871
+ button2.type = "button";
17872
+ button2.className = "settings-command-button settings-command-button--secondary settings-connection-update-all";
17873
+ button2.dataset.connectionAction = "update-all";
17874
+ button2.disabled = signal.aborted || Boolean(batch?.running) || !count3;
17875
+ button2.title = messages.harnessUpdateAllDescription;
17876
+ const label = batch?.running ? `${messages.harnessUpdateAllProgress} ${batch.succeeded + batch.failed}/${batch.total}` : count3 ? `${messages.harnessUpdateAll} (${count3})` : messages.harnessUpdateAll;
17877
+ button2.append(createRendererSettingsIcon("download", 15), label);
17878
+ button2.addEventListener("click", () => {
17879
+ if (!button2.disabled) void start(hostId2, panels);
17880
+ });
17881
+ const result = document2.createElement("p");
17882
+ result.className = "settings-connection-update-result";
17883
+ result.setAttribute("role", "status");
17884
+ result.hidden = !batch || batch.running;
17885
+ if (batch && !batch.running) {
17886
+ result.textContent = messages.harnessUpdateAllResult.replace("{succeeded}", String(batch.succeeded)).replace("{failed}", String(batch.failed));
17887
+ }
17888
+ return { button: button2, result };
17889
+ }
17890
+ };
17640
17891
  }
17641
17892
 
17642
17893
  // src/settings/connections-page.ts
@@ -17804,6 +18055,7 @@ ${error51.stderrTail}`] : []
17804
18055
  status.dataset.connectionTone = connectionStatusTone(item.availability, item.error !== null);
17805
18056
  status.setAttribute("role", "cell");
17806
18057
  status.textContent = item.error?.code === "authenticationRequired" ? messages.connectionLoginRequired : item.error?.code === "configurationRequired" ? messages.connectionConfigurationRequired : connectionStatusLabel(item.availability, messages, item.error !== null);
18058
+ const version2 = item.version;
17807
18059
  const action = document2.createElement("div");
17808
18060
  action.className = "settings-connection-row__action";
17809
18061
  action.setAttribute("role", "cell");
@@ -17833,6 +18085,31 @@ ${error51.stderrTail}`] : []
17833
18085
  });
17834
18086
  action.append(viewError);
17835
18087
  }
18088
+ if (version2?.state?.updateAvailable && !version2.updating) {
18089
+ const available = document2.createElement("span");
18090
+ available.className = "settings-connection-version-available";
18091
+ available.textContent = messages.harnessVersionAvailable;
18092
+ available.title = `${messages.harnessVersionCurrent}: ${version2.state.currentVersion ?? "\u2014"}
18093
+ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages.harnessVersion)}`;
18094
+ action.append(available);
18095
+ }
18096
+ if (version2?.state?.updateAvailable && version2.state.canUpdate) {
18097
+ const update = document2.createElement("button");
18098
+ update.type = "button";
18099
+ update.className = "settings-connection-install-link";
18100
+ update.dataset.connectionAction = "update";
18101
+ update.disabled = version2.updating;
18102
+ update.title = messages.harnessVersionUpdate;
18103
+ update.setAttribute("aria-label", `${messages.harnessVersionUpdate}: ${item.name}`);
18104
+ update.append(createRendererSettingsIcon("download", 17));
18105
+ update.addEventListener("click", (event) => {
18106
+ event.stopPropagation();
18107
+ if (update.disabled) return;
18108
+ void version2.update();
18109
+ select();
18110
+ });
18111
+ action.append(update);
18112
+ }
17836
18113
  if (group) {
17837
18114
  const toggle = document2.createElement("button");
17838
18115
  toggle.type = "button";
@@ -18124,8 +18401,6 @@ ${error51.stderrTail}`] : []
18124
18401
  let selectedHostId = null;
18125
18402
  let selectedItemKey = null;
18126
18403
  let launchControls = null;
18127
- const versionPanels = /* @__PURE__ */ new Map();
18128
- const updatingVersions = /* @__PURE__ */ new Set();
18129
18404
  const hostNames = /* @__PURE__ */ new Map();
18130
18405
  let latestSnapshot = null;
18131
18406
  let disposeHostScroller = () => void 0;
@@ -18154,7 +18429,15 @@ ${error51.stderrTail}`] : []
18154
18429
  renderFrame = null;
18155
18430
  };
18156
18431
  const diagnostics = getDiagnostics();
18432
+ const versions = createConnectionVersions(
18433
+ document2,
18434
+ messages,
18435
+ context.signal,
18436
+ diagnostics,
18437
+ scheduleRender
18438
+ );
18157
18439
  const installs = diagnostics ? harnessInstallStore(diagnostics) : null;
18440
+ const updates = createConnectionUpdates(document2, messages, context.signal, scheduleRender);
18158
18441
  const runRefresh = () => {
18159
18442
  if (pending || !diagnostics) return;
18160
18443
  pending = true;
@@ -18290,10 +18573,13 @@ ${error51.stderrTail}`] : []
18290
18573
  const agent = item.agentSnapshot?.agent;
18291
18574
  if (!agent) return item;
18292
18575
  const state = installs?.get(selectedHost.hostId, agent);
18576
+ const availability = state?.status ?? item.availability;
18577
+ const version2 = item.agentSnapshot?.availability !== "notInstalled" && availability !== "installing" && availability !== "checking" ? versions.get(selectedHost.hostId, agent) : void 0;
18293
18578
  return {
18294
18579
  ...item,
18295
18580
  ...state ? { availability: state.status } : {},
18296
- ...updatingVersions.has(JSON.stringify([selectedHost.hostId, agent])) ? { availability: "updating" } : {},
18581
+ ...version2 ? { version: version2 } : {},
18582
+ ...version2?.updating ? { availability: "updating" } : {},
18297
18583
  ...state?.error ? { installError: state.error } : {},
18298
18584
  ...diagnostics?.installation && harnessHasInstallCommands(item.agentSnapshot?.plugin) ? {
18299
18585
  install: () => {
@@ -18330,30 +18616,7 @@ ${error51.stderrTail}`] : []
18330
18616
  runRefresh
18331
18617
  );
18332
18618
  launchControls = { hostId: selectedHost.hostId, itemKey: item.key, element };
18333
- const agent = item.agentSnapshot?.agent;
18334
- const installation = diagnostics?.installation?.bind(diagnostics);
18335
- if (agent && item.agentSnapshot?.availability !== "notInstalled" && item.availability !== "installing" && item.availability !== "checking" && installation) {
18336
- const key = JSON.stringify([selectedHost.hostId, agent]);
18337
- let panel = versionPanels.get(key);
18338
- if (!panel) {
18339
- const hostId2 = selectedHost.hostId;
18340
- panel = createHarnessVersionPanel(document2, messages, context.signal, agent, {
18341
- async run(action) {
18342
- if (action !== "update") return installation(hostId2, agent, action);
18343
- updatingVersions.add(key);
18344
- if (diagnostics) render(diagnostics.snapshot());
18345
- try {
18346
- return await installation(hostId2, agent, action);
18347
- } finally {
18348
- updatingVersions.delete(key);
18349
- if (!context.signal.aborted && diagnostics) render(diagnostics.snapshot());
18350
- }
18351
- }
18352
- });
18353
- versionPanels.set(key, panel);
18354
- }
18355
- inspector.append(panel);
18356
- }
18619
+ if (item.version) inspector.append(item.version.element);
18357
18620
  };
18358
18621
  const pinnedItem = items.find((item) => item.key === "renderer-adapter");
18359
18622
  if (pinnedItem) {
@@ -18509,7 +18772,14 @@ ${error51.stderrTail}`] : []
18509
18772
  }
18510
18773
  const selectedItem = items.find((item) => item.key === selectedItemKey) ?? items[0];
18511
18774
  if (selectedItem) selectItem(selectedItem);
18512
- list.append(hostStrip, tableHeader, rows);
18775
+ const updateControls = updates.createControls(
18776
+ selectedHost.hostId,
18777
+ items.flatMap((item) => item.version ? [item.version] : [])
18778
+ );
18779
+ const hostHeader = document2.createElement("div");
18780
+ hostHeader.className = "settings-connection-host-header";
18781
+ hostHeader.append(hostStrip, updateControls.button);
18782
+ list.append(hostHeader, updateControls.result, tableHeader, rows);
18513
18783
  layout.append(list, inspector);
18514
18784
  content.replaceChildren(layout);
18515
18785
  disposeHostScroller = configureHostScroller(
@@ -18814,10 +19084,17 @@ ${error51.stderrTail}`] : []
18814
19084
  connectionInstallRunning: "Installing on the selected Host. You can leave this page; login or setup may still be required afterward.",
18815
19085
  harnessVersion: harnessVersionEnglish,
18816
19086
  harnessVersionTitle: "Harness CLI version",
19087
+ harnessVersionApplicationTitle: "Application version",
18817
19088
  harnessVersionCurrent: "Current version",
18818
19089
  harnessVersionLatest: "Latest version",
18819
19090
  harnessVersionChecking: "Checking versions\u2026",
19091
+ harnessVersionAvailable: "Update available",
18820
19092
  harnessVersionUpdate: "Update",
19093
+ harnessVersionDownloadUpdate: "Download update",
19094
+ harnessUpdateAll: "Update all",
19095
+ harnessUpdateAllProgress: "Updating",
19096
+ harnessUpdateAllDescription: "Update all eligible Harnesses on the selected Host.",
19097
+ harnessUpdateAllResult: "Updated {succeeded}; failed {failed}.",
18821
19098
  harnessVersionUpdating: "Updating\u2026",
18822
19099
  harnessVersionUpToDate: "Up to date",
18823
19100
  harnessVersionUpdated: "Update verified. New sessions will use the new version.",
@@ -19087,10 +19364,17 @@ ${error51.stderrTail}`] : []
19087
19364
  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",
19088
19365
  harnessVersion: harnessVersionChinese,
19089
19366
  harnessVersionTitle: "Harness CLI \u7248\u672C",
19367
+ harnessVersionApplicationTitle: "\u5E94\u7528\u7248\u672C",
19090
19368
  harnessVersionCurrent: "\u5F53\u524D\u7248\u672C",
19091
19369
  harnessVersionLatest: "\u6700\u65B0\u7248\u672C",
19092
19370
  harnessVersionChecking: "\u6B63\u5728\u68C0\u67E5\u7248\u672C\u2026",
19371
+ harnessVersionAvailable: "\u6709\u66F4\u65B0",
19093
19372
  harnessVersionUpdate: "\u66F4\u65B0",
19373
+ harnessVersionDownloadUpdate: "\u4E0B\u8F7D\u66F4\u65B0",
19374
+ harnessUpdateAll: "\u5168\u90E8\u66F4\u65B0",
19375
+ harnessUpdateAllProgress: "\u66F4\u65B0\u4E2D",
19376
+ harnessUpdateAllDescription: "\u66F4\u65B0\u5F53\u524D Host \u4E0A\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u4E14\u6709\u65B0\u7248\u672C\u7684 Harness\u3002",
19377
+ harnessUpdateAllResult: "\u5DF2\u66F4\u65B0 {succeeded} \u9879\uFF0C{failed} \u9879\u5931\u8D25\u3002",
19094
19378
  harnessVersionUpdating: "\u6B63\u5728\u66F4\u65B0\u2026",
19095
19379
  harnessVersionUpToDate: "\u5DF2\u662F\u6700\u65B0",
19096
19380
  harnessVersionUpdated: "\u5DF2\u786E\u8BA4\u66F4\u65B0\u6210\u529F\uFF0C\u65B0\u4F1A\u8BDD\u5C06\u4F7F\u7528\u65B0\u7248\u672C\u3002",
@@ -22236,7 +22520,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22236
22520
  })
22237
22521
  );
22238
22522
  }
22239
- if (label) label.textContent = percent2;
22523
+ if (label) label.textContent = `${Math.round(remaining)}%`;
22240
22524
  control.root.style.display = "inline-flex";
22241
22525
  control.trigger.setAttribute("aria-label", title);
22242
22526
  control.trigger.title = title;
@@ -22244,6 +22528,148 @@ Codex Fast mode prioritizes requests and may increase usage.`
22244
22528
  return true;
22245
22529
  }
22246
22530
 
22531
+ // src/renderer-composer-indicators.ts
22532
+ var SHOW_WITHIN_SECONDS = 50 * 60;
22533
+ function mountRendererComposerIndicators(composer) {
22534
+ const doc = composer.ownerDocument;
22535
+ ensureRendererTriggerChipStyle(doc);
22536
+ const root = doc.createElement("div");
22537
+ root.setAttribute("data-codexhost-composer-indicators", "true");
22538
+ applyRendererTriggerChipSqueezeRoot(root, "128px", "0");
22539
+ Object.assign(root.style, { alignItems: "center", gap: "2px", flexShrink: "100000" });
22540
+ let indicators = [];
22541
+ let locale = "en";
22542
+ let timer;
22543
+ let anchor = null;
22544
+ let contentKey = "";
22545
+ let tip = null;
22546
+ const hideTip = () => {
22547
+ tip?.remove();
22548
+ tip = null;
22549
+ };
22550
+ const showTip = (chip, textContent) => {
22551
+ hideTip();
22552
+ const el = doc.createElement("div");
22553
+ el.setAttribute("role", "tooltip");
22554
+ el.textContent = textContent;
22555
+ Object.assign(el.style, {
22556
+ position: "fixed",
22557
+ zIndex: "2147483647",
22558
+ pointerEvents: "none",
22559
+ padding: "4px 8px",
22560
+ borderRadius: "8px",
22561
+ fontSize: "12px",
22562
+ whiteSpace: "nowrap",
22563
+ background: "rgba(30, 30, 30, 0.92)",
22564
+ color: "#fff",
22565
+ visibility: "hidden"
22566
+ });
22567
+ (doc.body ?? doc.documentElement).append(el);
22568
+ const rect = chip.getBoundingClientRect();
22569
+ const width = el.offsetWidth;
22570
+ const left = Math.max(
22571
+ 4,
22572
+ Math.min(rect.left + rect.width / 2 - width / 2, window.innerWidth - width - 4)
22573
+ );
22574
+ el.style.left = `${left}px`;
22575
+ el.style.top = `${Math.max(4, rect.top - el.offsetHeight - 6)}px`;
22576
+ el.style.visibility = "visible";
22577
+ tip = el;
22578
+ };
22579
+ const refresh = () => {
22580
+ const now = Date.now() / 1e3;
22581
+ const active = indicators.filter(
22582
+ (indicator) => indicator.expiresAtUnix > now && indicator.expiresAtUnix - now <= SHOW_WITHIN_SECONDS
22583
+ );
22584
+ const nextKey = JSON.stringify([
22585
+ locale,
22586
+ active.map((indicator) => [indicator, Math.ceil((indicator.expiresAtUnix - now) / 60)])
22587
+ ]);
22588
+ if (nextKey !== contentKey) {
22589
+ contentKey = nextKey;
22590
+ hideTip();
22591
+ root.replaceChildren();
22592
+ for (const indicator of active) {
22593
+ const minutes = Math.ceil((indicator.expiresAtUnix - now) / 60);
22594
+ const remaining = locale === "zh-CN" ? `\u5269\u4F59 ${minutes} \u5206\u949F` : `${minutes}m remaining`;
22595
+ const label = indicator.label ? localizeHarnessText(indicator.label, locale) : "";
22596
+ const tooltip = indicator.tooltip ? localizeHarnessText(indicator.tooltip, locale) : "";
22597
+ const chip = doc.createElement("span");
22598
+ chip.className = TRIGGER_CHIP_CLASS;
22599
+ chip.setAttribute("data-indicator-id", indicator.id);
22600
+ const summary = [label, tooltip, remaining].filter(Boolean).join(" \xB7 ");
22601
+ chip.setAttribute("aria-label", summary);
22602
+ chip.addEventListener("mouseenter", () => showTip(chip, summary));
22603
+ chip.addEventListener("mouseleave", hideTip);
22604
+ Object.assign(chip.style, {
22605
+ height: "28px",
22606
+ padding: "0 6px",
22607
+ gap: "4px",
22608
+ fontSize: "12px",
22609
+ cursor: "default",
22610
+ flexShrink: "0"
22611
+ });
22612
+ const icon2 = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
22613
+ icon2.setAttribute("viewBox", "0 0 24 24");
22614
+ icon2.setAttribute("width", "14");
22615
+ icon2.setAttribute("height", "14");
22616
+ icon2.setAttribute("fill", "none");
22617
+ icon2.setAttribute("stroke", "currentColor");
22618
+ icon2.setAttribute("stroke-width", "1.5");
22619
+ icon2.setAttribute("aria-hidden", "true");
22620
+ const path = doc.createElementNS("http://www.w3.org/2000/svg", "path");
22621
+ path.setAttribute("d", "M12 8v4l3 2M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0");
22622
+ icon2.append(path);
22623
+ const text = doc.createElement("span");
22624
+ text.textContent = `${minutes}m`;
22625
+ chip.append(icon2, text);
22626
+ root.append(chip);
22627
+ }
22628
+ }
22629
+ const visible = active.length > 0 && anchor !== null;
22630
+ if (root.hidden === visible) root.hidden = !visible;
22631
+ const display = visible ? "inline-flex" : "none";
22632
+ if (root.style.display !== display) root.style.display = display;
22633
+ const visibility = root.scrollWidth > root.clientWidth ? "hidden" : "visible";
22634
+ if (root.style.visibility !== visibility) root.style.visibility = visibility;
22635
+ const pending = indicators.some(({ expiresAtUnix }) => expiresAtUnix > now);
22636
+ if (!pending && timer !== void 0) {
22637
+ clearInterval(timer);
22638
+ timer = void 0;
22639
+ }
22640
+ };
22641
+ const observer = new ResizeObserver(refresh);
22642
+ observer.observe(root);
22643
+ refresh();
22644
+ return {
22645
+ root,
22646
+ place(next) {
22647
+ anchor = next;
22648
+ if (!next?.parentElement) {
22649
+ anchor = null;
22650
+ root.remove();
22651
+ } else if (next.nextElementSibling !== root) {
22652
+ next.parentElement.insertBefore(root, next.nextSibling);
22653
+ }
22654
+ refresh();
22655
+ },
22656
+ render(next, nextLocale) {
22657
+ indicators = next;
22658
+ locale = nextLocale;
22659
+ refresh();
22660
+ if (timer === void 0 && indicators.some(({ expiresAtUnix }) => expiresAtUnix > Date.now() / 1e3)) {
22661
+ timer = setInterval(refresh, 6e4);
22662
+ }
22663
+ },
22664
+ dispose() {
22665
+ observer.disconnect();
22666
+ hideTip();
22667
+ if (timer !== void 0) clearInterval(timer);
22668
+ root.remove();
22669
+ }
22670
+ };
22671
+ }
22672
+
22247
22673
  // src/renderer-harness-command-control.ts
22248
22674
  var CONTROL_ATTRIBUTE2 = "data-codexhost-harness-command-control";
22249
22675
  var SVG_NS2 = "http://www.w3.org/2000/svg";
@@ -22376,7 +22802,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22376
22802
  return controlDescription(button2);
22377
22803
  }
22378
22804
  function isOwnedRendererControl(element) {
22379
- 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");
22805
+ 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");
22380
22806
  }
22381
22807
  function isComposerSubmitButton(button2) {
22382
22808
  if (button2.type === "submit") return true;
@@ -22674,6 +23100,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
22674
23100
  refreshTrailingClusterPlacement(control);
22675
23101
  refreshUsagePlacement(control);
22676
23102
  refreshCreditsPlacement(control);
23103
+ control.composerIndicators?.place(
23104
+ control.nativePermissionModeControlVerified ? control.nativePermissionModeControl?.element ?? null : null
23105
+ );
22677
23106
  setNativeControlHidden(control.nativeModelControl, hideModel);
22678
23107
  setNativeControlHidden(control.nativeContextUsageControl, false);
22679
23108
  setNativeControlHidden(control.nativePermissionModeControl, hidePermissionMode);
@@ -22700,6 +23129,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22700
23129
  onSelectPermissionMode
22701
23130
  );
22702
23131
  const credits = mountRendererCreditsControl(composerId);
23132
+ const composerIndicators = mountRendererComposerIndicators(composer);
22703
23133
  const toolbar = sendButton.parentElement;
22704
23134
  const harnessCommands = mountRendererHarnessCommandControl(
22705
23135
  toolbar ?? composer,
@@ -22746,6 +23176,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22746
23176
  nativeContextUsageControl,
22747
23177
  nativePermissionModeControlVerified,
22748
23178
  credits,
23179
+ composerIndicators,
22749
23180
  usage: null,
22750
23181
  harnessCommands,
22751
23182
  sendButton,
@@ -22811,6 +23242,10 @@ Codex Fast mode prioritizes requests and may increase usage.`
22811
23242
  permissionModeVisible,
22812
23243
  locale
22813
23244
  );
23245
+ control.composerIndicators?.render(
23246
+ permissionModeVisible ? usage?.composerIndicators ?? [] : [],
23247
+ locale
23248
+ );
22814
23249
  if (control.usage) {
22815
23250
  renderRendererUsageControl(control.usage, usage, locale);
22816
23251
  }
@@ -22830,6 +23265,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22830
23265
  restoreNativeControl(control.nativeModelControl);
22831
23266
  restoreNativeControl(control.nativeContextUsageControl);
22832
23267
  restoreNativeControl(control.nativePermissionModeControl);
23268
+ control.composerIndicators?.dispose();
22833
23269
  control.credits.dispose();
22834
23270
  control.usage?.dispose();
22835
23271
  control.usage = null;
@@ -23384,6 +23820,26 @@ Codex Fast mode prioritizes requests and may increase usage.`
23384
23820
  };
23385
23821
  }
23386
23822
 
23823
+ // src/renderer-installation-client.ts
23824
+ async function runRendererInstallation(send, input) {
23825
+ const params = harnessInstallationParamsSchema.parse(input);
23826
+ const options = { priority: "background", timeoutMs: 15e3 };
23827
+ let task = harnessInstallationTaskSchema.parse(
23828
+ await send(HARNESS_INSTALLATION_START_METHOD, params, options)
23829
+ );
23830
+ const taskId = task.taskId;
23831
+ while (task.status === "running") {
23832
+ await new Promise((resolve) => globalThis.setTimeout(resolve, 500));
23833
+ task = harnessInstallationTaskSchema.parse(
23834
+ await send(HARNESS_INSTALLATION_STATUS_METHOD, { taskId }, options)
23835
+ );
23836
+ if (task.taskId !== taskId) throw new Error("Harness installation task identity changed");
23837
+ }
23838
+ if (task.status === "failed")
23839
+ throw Object.assign(new Error(task.error.message), { code: task.error.code });
23840
+ return task.result;
23841
+ }
23842
+
23387
23843
  // src/renderer-request-sender.ts
23388
23844
  var RendererMethodUnavailableError = class extends Error {
23389
23845
  constructor(method, cause) {
@@ -23573,7 +24029,15 @@ Codex Fast mode prioritizes requests and may increase usage.`
23573
24029
  };
23574
24030
  const inspectHarness = async (input, options) => {
23575
24031
  const params = harnessInspectParamsSchema.parse(input);
23576
- const result = await manager.sendRequest(HARNESS_INSPECT_METHOD, params, options);
24032
+ let result;
24033
+ try {
24034
+ result = await manager.sendRequest(HARNESS_INSPECT_METHOD, params, options);
24035
+ } catch (error51) {
24036
+ if (!params.purpose || !isInvalidParams(error51)) throw error51;
24037
+ const full = { ...params };
24038
+ delete full.purpose;
24039
+ result = await manager.sendRequest(HARNESS_INSPECT_METHOD, full, options);
24040
+ }
23577
24041
  return harnessInspectionSchema.parse(result);
23578
24042
  };
23579
24043
  const inspectHarnessCommands = async (input) => {
@@ -23613,13 +24077,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23613
24077
  };
23614
24078
  return Object.freeze({
23615
24079
  async installation(input) {
23616
- return harnessInstallationStateSchema.parse(
23617
- await manager.sendRequest(
23618
- HARNESS_INSTALLATION_METHOD,
23619
- harnessInstallationParamsSchema.parse(input),
23620
- { priority: "interactive" }
23621
- )
23622
- );
24080
+ return runRendererInstallation(manager.sendRequest, input);
23623
24081
  },
23624
24082
  async getHarnessDisplaySettings() {
23625
24083
  return harnessDisplaySettingsSchema.parse(
@@ -23835,6 +24293,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
23835
24293
  }
23836
24294
  });
23837
24295
  }
24296
+ function isInvalidParams(error51) {
24297
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === -32602;
24298
+ }
23838
24299
 
23839
24300
  // src/renderer-external-queue.ts
23840
24301
  function isRecord6(value) {
@@ -23870,6 +24331,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23870
24331
  }
23871
24332
 
23872
24333
  // src/renderer-external-steering.ts
24334
+ var THREAD_STEERING_INSPECT_METHOD = "codexhost/thread/steering/inspect";
23873
24335
  var THREAD_OWNERSHIP_LIST_METHOD2 = "codexhost/thread/ownership/list";
23874
24336
  function isRecord7(value) {
23875
24337
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -23953,6 +24415,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23953
24415
  const originalSteer = manager.steerTurn;
23954
24416
  const originalSend = manager.sendRequest;
23955
24417
  const routes = /* @__PURE__ */ new Map();
24418
+ const activeTurnSteers = /* @__PURE__ */ new Map();
23956
24419
  const pending = /* @__PURE__ */ new Map();
23957
24420
  let disposed = false;
23958
24421
  const send = function(method, params, options) {
@@ -23968,6 +24431,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
23968
24431
  return originalSend.call(manager, method, input, options);
23969
24432
  });
23970
24433
  }
24434
+ if (method === "turn/steer" && isRecord7(params) && typeof params.threadId === "string" && activeTurnSteers.has(params.threadId)) {
24435
+ params = { ...params, [THREAD_STEER_DELIVERY_PARAM]: "activeTurn" };
24436
+ }
23971
24437
  return originalSend.call(manager, method, params, options);
23972
24438
  }
23973
24439
  if (disposed) return Promise.reject(new Error("External steering binding was disposed"));
@@ -23982,7 +24448,8 @@ Codex Fast mode prioritizes requests and may increase usage.`
23982
24448
  input: params.input,
23983
24449
  additionalContext: params.additionalContext,
23984
24450
  responsesapiClientMetadata: params.responsesapiClientMetadata,
23985
- ...params[THREAD_REFERENCE_SOURCE_HOST_PARAM] !== void 0 ? { [THREAD_REFERENCE_SOURCE_HOST_PARAM]: params[THREAD_REFERENCE_SOURCE_HOST_PARAM] } : {}
24451
+ ...params[THREAD_REFERENCE_SOURCE_HOST_PARAM] !== void 0 ? { [THREAD_REFERENCE_SOURCE_HOST_PARAM]: params[THREAD_REFERENCE_SOURCE_HOST_PARAM] } : {},
24452
+ [THREAD_STEER_DELIVERY_PARAM]: "newTurn"
23986
24453
  },
23987
24454
  options
23988
24455
  )
@@ -24004,6 +24471,34 @@ Codex Fast mode prioritizes requests and may increase usage.`
24004
24471
  };
24005
24472
  });
24006
24473
  };
24474
+ const inspectDelivery = async (threadId, text) => {
24475
+ try {
24476
+ const response = await originalSend.call(manager, THREAD_STEERING_INSPECT_METHOD, {
24477
+ threadId,
24478
+ ...text !== void 0 ? { text } : {}
24479
+ });
24480
+ return threadSteeringInspectResultSchema.parse(response).delivery;
24481
+ } catch (error51) {
24482
+ if (!isUnsupportedMethod(error51, THREAD_STEERING_INSPECT_METHOD)) throw error51;
24483
+ }
24484
+ let ownershipResponse;
24485
+ try {
24486
+ ownershipResponse = await originalSend.call(manager, THREAD_OWNERSHIP_LIST_METHOD2, {
24487
+ threadIds: [threadId]
24488
+ });
24489
+ } catch (error51) {
24490
+ if (!isUnsupportedMethod(error51, THREAD_OWNERSHIP_LIST_METHOD2)) throw error51;
24491
+ await verifyNativeCodexThread(
24492
+ (method, params) => originalSend.call(manager, method, params),
24493
+ threadId
24494
+ );
24495
+ return "official";
24496
+ }
24497
+ const ownership = threadOwnershipListResultSchema.parse(ownershipResponse);
24498
+ const owner = ownership.threads.find((thread) => thread.threadId === threadId)?.owner;
24499
+ if (!owner) throw new Error("Thread ownership could not be resolved for steering");
24500
+ return owner === "codex" ? "official" : "newTurn";
24501
+ };
24007
24502
  const steer = async function(...args) {
24008
24503
  const [
24009
24504
  threadId,
@@ -24026,25 +24521,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
24026
24521
  expectedTurnId = host.getActiveTurnId(threadId);
24027
24522
  } catch {
24028
24523
  }
24029
- let ownershipResponse;
24030
- try {
24031
- ownershipResponse = await originalSend.call(manager, THREAD_OWNERSHIP_LIST_METHOD2, {
24032
- threadIds: [threadId]
24033
- });
24034
- } catch (error51) {
24035
- if (!isUnsupportedMethod(error51, THREAD_OWNERSHIP_LIST_METHOD2)) throw error51;
24036
- await verifyNativeCodexThread(
24037
- (method, params) => originalSend.call(manager, method, params),
24038
- threadId
24039
- );
24040
- if (disposed) throw new Error("External steering binding was disposed");
24041
- return originalSteer.apply(manager, args);
24042
- }
24043
- const ownership = threadOwnershipListResultSchema.parse(ownershipResponse);
24044
- const owner = ownership.threads.find((thread) => thread.threadId === threadId)?.owner;
24045
- if (!owner) throw new Error("Thread ownership could not be resolved for steering");
24524
+ const text = Array.isArray(input) ? input.filter((item) => isRecord7(item) && typeof item.text === "string").map((item) => item.text).join("\n") : void 0;
24525
+ const delivery = await inspectDelivery(threadId, text);
24046
24526
  if (disposed) throw new Error("External steering binding was disposed");
24047
- if (owner === "codex") return originalSteer.apply(manager, args);
24527
+ if (delivery === "official") return originalSteer.apply(manager, args);
24528
+ if (delivery === "activeTurn") {
24529
+ activeTurnSteers.set(threadId, (activeTurnSteers.get(threadId) ?? 0) + 1);
24530
+ try {
24531
+ return await originalSteer.apply(manager, args);
24532
+ } finally {
24533
+ const remaining = (activeTurnSteers.get(threadId) ?? 1) - 1;
24534
+ if (remaining > 0) activeTurnSteers.set(threadId, remaining);
24535
+ else activeTurnSteers.delete(threadId);
24536
+ }
24537
+ }
24048
24538
  if (!host) throw new Error("Desktop turn submission binding is unavailable");
24049
24539
  const currentRole = manager.getStreamRole?.(threadId);
24050
24540
  if (isRecord7(currentRole) && currentRole.role === "follower")
@@ -24289,9 +24779,9 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
24289
24779
  }
24290
24780
 
24291
24781
  // src/renderer-thread-reference-capability.ts
24292
- var record3 = (value) => typeof value === "object" && value !== null;
24782
+ var record4 = (value) => typeof value === "object" && value !== null;
24293
24783
  function restoreThreadReferenceCapability(client, manager, isCurrent) {
24294
- if (!record3(manager) || !record3(manager.storage)) return client;
24784
+ if (!record4(manager) || !record4(manager.storage)) return client;
24295
24785
  const storage = manager.storage;
24296
24786
  const read = storage.readValue;
24297
24787
  const write = storage.writeValue;
@@ -24326,14 +24816,14 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
24326
24816
  }
24327
24817
 
24328
24818
  // src/renderer-thread-reference-context.ts
24329
- var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
24819
+ var record5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
24330
24820
  function createThreadReferenceContext(manager, hostId2, isCurrent) {
24331
24821
  const prepared = /* @__PURE__ */ new WeakMap();
24332
24822
  return (params) => {
24333
- if (hostId2 === "local" || !record4(manager) || typeof manager.getConversation !== "function" || !record4(params) || typeof params.threadId !== "string")
24823
+ if (hostId2 === "local" || !record5(manager) || typeof manager.getConversation !== "function" || !record5(params) || typeof params.threadId !== "string")
24334
24824
  return params;
24335
24825
  const conversation = manager.getConversation.call(manager, params.threadId);
24336
- if (!record4(conversation) || conversation.id !== params.threadId || conversation.modelProvider !== "codexhost")
24826
+ if (!record5(conversation) || conversation.id !== params.threadId || conversation.modelProvider !== "codexhost")
24337
24827
  return params;
24338
24828
  if (!isCurrent()) throw new Error("Thread reference connection is no longer available");
24339
24829
  const cached2 = prepared.get(params);
@@ -25855,6 +26345,11 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25855
26345
  var GET_LOCALE_OVERRIDE_URL = "vscode://codex/get-setting";
25856
26346
  var GET_LOCALE_INFO_URL = "vscode://codex/locale-info";
25857
26347
  var DEFAULT_LOCALE_REQUEST_TIMEOUT_MS = 750;
26348
+ function isNativeLocaleServices(value) {
26349
+ if (!isRecord14(value) || !Object.hasOwn(value, "appInfo")) return false;
26350
+ const appInfo = value.appInfo;
26351
+ return appInfo != null && typeof appInfo.getLocaleInfo === "function";
26352
+ }
25858
26353
  function isRecord14(value) {
25859
26354
  return typeof value === "object" && value !== null && !Array.isArray(value);
25860
26355
  }
@@ -25900,7 +26395,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25900
26395
  }
25901
26396
  function sendFixedLocaleRequest(request, ownerWindow, signal, timeoutMs) {
25902
26397
  const bridge = ownerWindow.electronBridge;
25903
- if (typeof bridge?.sendMessageFromView !== "function") {
26398
+ if (request.kind !== "locale-info" && typeof bridge?.sendMessageFromView !== "function") {
25904
26399
  return Promise.reject(new Error("Codex locale bridge is unavailable"));
25905
26400
  }
25906
26401
  if (signal?.aborted) return Promise.reject(abortError2());
@@ -25948,13 +26443,21 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25948
26443
  ...request.body === void 0 ? {} : { body: request.body },
25949
26444
  reportUploadProgress: false
25950
26445
  };
25951
- try {
25952
- Promise.resolve(bridge.sendMessageFromView(message2)).catch((error51) => {
25953
- settle(() => reject(error51));
25954
- });
25955
- } catch (error51) {
25956
- settle(() => reject(error51));
25957
- }
26446
+ void (async () => {
26447
+ if (request.kind === "locale-info") {
26448
+ const native = await loadCodexNativeServices(ownerWindow, isNativeLocaleServices);
26449
+ if (settled) return;
26450
+ if (native) {
26451
+ const response = await native.appInfo.getLocaleInfo();
26452
+ settle(() => resolve(response));
26453
+ return;
26454
+ }
26455
+ }
26456
+ if (typeof bridge?.sendMessageFromView !== "function") {
26457
+ throw new Error("Codex locale bridge is unavailable");
26458
+ }
26459
+ await bridge.sendMessageFromView(message2);
26460
+ })().catch((error51) => settle(() => reject(error51)));
25958
26461
  });
25959
26462
  }
25960
26463
  async function readLocaleOverride(ownerWindow, signal, timeoutMs) {
@@ -26064,6 +26567,34 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
26064
26567
  return names.size === 1 ? names.values().next().value ?? model2 : model2;
26065
26568
  }
26066
26569
 
26570
+ // src/console/usage/usage-hosts.ts
26571
+ var LOCAL_USAGE_HOST_ID = "local";
26572
+ function usageHostOptions(hosts, localLabel, selected) {
26573
+ const options = [{ key: LOCAL_USAGE_HOST_ID, label: localLabel }];
26574
+ const seen = /* @__PURE__ */ new Set([LOCAL_USAGE_HOST_ID]);
26575
+ const add = (hostId2, label) => {
26576
+ if (hostId2.length === 0 || seen.has(hostId2)) return;
26577
+ seen.add(hostId2);
26578
+ options.push({ key: hostId2, label: label !== void 0 && label.length > 0 ? label : hostId2 });
26579
+ };
26580
+ for (const host of hosts ?? []) add(host.hostId, host.label);
26581
+ if (!seen.has(selected)) add(selected, void 0);
26582
+ return options;
26583
+ }
26584
+ function createLatestKeyGuard() {
26585
+ let generation = 0;
26586
+ let key;
26587
+ return {
26588
+ begin(next) {
26589
+ generation += 1;
26590
+ key = next;
26591
+ const mine = generation;
26592
+ const selected = next;
26593
+ return () => generation === mine && Object.is(key, selected);
26594
+ }
26595
+ };
26596
+ }
26597
+
26067
26598
  // src/console/usage/format.ts
26068
26599
  var MEASURES = ["cost", "tokens"];
26069
26600
  function fill(template, values) {
@@ -27350,6 +27881,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27350
27881
  const range = text("range");
27351
27882
  const measure = text("measure");
27352
27883
  const granularity = text("granularity");
27884
+ const hostId2 = text("hostId");
27353
27885
  const harness2 = text("harness");
27354
27886
  const model2 = text("model");
27355
27887
  const project3 = text("project");
@@ -27358,6 +27890,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27358
27890
  ...range && USAGE_STATISTICS_RANGES.includes(range) ? { range } : {},
27359
27891
  ...measure && MEASURES.includes(measure) ? { measure } : {},
27360
27892
  ...granularity === "day" || granularity === "week" || granularity === "month" ? { granularity } : {},
27893
+ ...hostId2 !== void 0 && hostId2.length > 0 ? { hostId: hostId2 } : {},
27361
27894
  ...harness2 !== void 0 ? { harness: harness2 } : {},
27362
27895
  ...model2 !== void 0 ? { model: model2 } : {},
27363
27896
  ...project3 !== void 0 ? { project: project3 } : {},
@@ -27396,7 +27929,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27396
27929
  day.setDate(day.getDate() - (day.getDay() + 6) % 7);
27397
27930
  return isoDate(day);
27398
27931
  }
27399
- function createUsageStatisticsPage(consoleMessages2, request, pageLocale) {
27932
+ function createUsageStatisticsPage(consoleMessages2, request, pageLocale, usageHosts) {
27400
27933
  const messages = consoleMessages2.usageStatistics;
27401
27934
  return Object.freeze({
27402
27935
  id: "usage-statistics",
@@ -27405,10 +27938,12 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27405
27938
  mount(context) {
27406
27939
  const document2 = context.content.ownerDocument;
27407
27940
  const locale = pageLocale ?? (document2.documentElement.lang || "en");
27941
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
27408
27942
  const count3 = (value) => count2(value, locale);
27409
27943
  const tokenCount2 = (totals2, value) => tokenCount(totals2, value, locale);
27410
27944
  const saved = readView();
27411
27945
  const view = {
27946
+ hostId: saved.hostId ?? LOCAL_USAGE_HOST_ID,
27412
27947
  range: saved.range ?? "30d",
27413
27948
  measure: saved.measure ?? "cost",
27414
27949
  granularity: saved.granularity ?? null,
@@ -27439,7 +27974,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27439
27974
  let projectLabel = /* @__PURE__ */ new Map();
27440
27975
  const projectName = (key) => key === UNKNOWN ? messages.unknownProject : projectLabel.get(key) ?? baseName(key);
27441
27976
  const measureLabel = (measure) => measure === "cost" ? messages.cost : messages.tokens;
27442
- const dateFormat = (options) => new Intl.DateTimeFormat(locale, options);
27977
+ const dateFormat = (options) => new Intl.DateTimeFormat(locale, { ...options, timeZone });
27443
27978
  const shortDate = dateFormat({ month: "short", day: "numeric" });
27444
27979
  const longDate = dateFormat({
27445
27980
  year: "numeric",
@@ -27458,10 +27993,51 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27458
27993
  const prices = createModelPricesDialog(
27459
27994
  document2,
27460
27995
  consoleMessages2,
27461
- request,
27996
+ (method, params2) => request(method, params2, view.hostId),
27462
27997
  context.signal,
27463
27998
  load
27464
27999
  );
28000
+ let offeredHosts = usageHosts ? [] : void 0;
28001
+ const hostCombo = new ComboBox(document2, {
28002
+ label: messages.host,
28003
+ searchLabel: messages.search,
28004
+ emptyLabel: messages.noMatches,
28005
+ signal: context.signal,
28006
+ searchable: false,
28007
+ display: (option) => `${messages.host} \xB7 ${option.label}`,
28008
+ onChange: (key) => {
28009
+ if (key === view.hostId) return;
28010
+ view.hostId = key;
28011
+ view.harness = "";
28012
+ view.model = "";
28013
+ view.project = "";
28014
+ date5 = null;
28015
+ unpricedOnly = false;
28016
+ result = null;
28017
+ error51 = null;
28018
+ loadedAt = 0;
28019
+ names.clear();
28020
+ save();
28021
+ hostOptions();
28022
+ render();
28023
+ void refreshHarnessNames();
28024
+ load();
28025
+ }
28026
+ });
28027
+ const hostOptions = () => {
28028
+ const options = usageHostOptions(offeredHosts, messages.localHost, view.hostId);
28029
+ hostCombo.element.hidden = options.length < 2;
28030
+ hostCombo.update(options, view.hostId === LOCAL_USAGE_HOST_ID ? "" : view.hostId);
28031
+ };
28032
+ const offerHosts = () => {
28033
+ if (!usageHosts) return;
28034
+ void usageHosts().then((hosts) => {
28035
+ if (context.signal.aborted) return;
28036
+ offeredHosts = hosts;
28037
+ hostOptions();
28038
+ }).catch(() => void 0);
28039
+ };
28040
+ hostOptions();
27465
28041
  const updated = h(document2, "span", { className: "console-muted console-usage-updated" });
27466
28042
  const refresh = h(
27467
28043
  document2,
@@ -27474,7 +28050,10 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27474
28050
  },
27475
28051
  createRendererSettingsIcon("refresh", 16)
27476
28052
  );
27477
- refresh.addEventListener("click", () => load());
28053
+ refresh.addEventListener("click", () => {
28054
+ offerHosts();
28055
+ load();
28056
+ });
27478
28057
  const auto = new ComboBox(document2, {
27479
28058
  label: messages.autoRefresh,
27480
28059
  searchLabel: messages.search,
@@ -27513,6 +28092,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
27513
28092
  document2,
27514
28093
  "div",
27515
28094
  { className: "console-usage-toolbar" },
28095
+ hostCombo.element,
27516
28096
  updated,
27517
28097
  refresh,
27518
28098
  auto.element,
@@ -28151,7 +28731,6 @@ ${detail}` : ""}`
28151
28731
  }
28152
28732
  function hourlyPanel(data) {
28153
28733
  const singleDay = data.filters.date !== null || data.range === "today";
28154
- const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
28155
28734
  return panel(
28156
28735
  singleDay ? messages.hourlyDay : messages.hourly,
28157
28736
  hourlyChart(document2, {
@@ -28166,7 +28745,7 @@ ${detail}` : ""}`
28166
28745
  weekStartsOnMonday: !locale.startsWith("en"),
28167
28746
  labels: { cost: messages.cost, tokens: messages.tokens }
28168
28747
  }),
28169
- h(document2, "span", { className: "console-muted console-usage-zone" }, zone)
28748
+ h(document2, "span", { className: "console-muted console-usage-zone" }, timeZone)
28170
28749
  );
28171
28750
  }
28172
28751
  function sessionsPanel(title, rows, tableKey) {
@@ -28313,6 +28892,7 @@ ${detail}` : ""}`
28313
28892
  const project3 = filterValue(view.project);
28314
28893
  return {
28315
28894
  range: view.range,
28895
+ timeZone,
28316
28896
  ...view.harness ? { harness: view.harness } : {},
28317
28897
  ...model2 !== void 0 ? { model: model2 } : {},
28318
28898
  ...project3 !== void 0 ? { project: project3 } : {},
@@ -28325,10 +28905,26 @@ ${detail}` : ""}`
28325
28905
  if (result && !result.reading.complete) poll = window.setTimeout(load, POLL_MS);
28326
28906
  else if (view.autoRefreshMs > 0) poll = window.setTimeout(load, view.autoRefreshMs);
28327
28907
  }
28908
+ const harnessNamesRead = createLatestKeyGuard();
28909
+ async function refreshHarnessNames() {
28910
+ const hostId2 = view.hostId;
28911
+ const isCurrent = harnessNamesRead.begin(hostId2);
28912
+ names.clear();
28913
+ try {
28914
+ const value = await request(HARNESS_PLUGIN_LIST_METHOD2, {}, hostId2);
28915
+ if (!isCurrent()) return;
28916
+ for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
28917
+ names.set(plugin.id, plugin.name);
28918
+ }
28919
+ } catch {
28920
+ if (!isCurrent()) return;
28921
+ }
28922
+ if (!context.signal.aborted) render();
28923
+ }
28328
28924
  function load() {
28329
28925
  window.clearTimeout(poll);
28330
28926
  const requested = params();
28331
- void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested), {
28927
+ void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested, view.hostId), {
28332
28928
  success(value) {
28333
28929
  const parsed = usageStatisticsResultSchema.safeParse(value);
28334
28930
  if (parsed.success) {
@@ -28353,12 +28949,8 @@ ${detail}` : ""}`
28353
28949
  }
28354
28950
  context.content.replaceChildren(header, prices.element, filters, notices, body);
28355
28951
  render();
28356
- void request(HARNESS_PLUGIN_LIST_METHOD2, {}).then((value) => {
28357
- for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
28358
- names.set(plugin.id, plugin.name);
28359
- }
28360
- if (!context.signal.aborted) render();
28361
- }).catch(() => void 0);
28952
+ offerHosts();
28953
+ void refreshHarnessNames();
28362
28954
  load();
28363
28955
  return () => {
28364
28956
  window.clearTimeout(poll);
@@ -28451,6 +29043,8 @@ ${detail}` : ""}`
28451
29043
  harness: "Harness",
28452
29044
  model: "Model",
28453
29045
  project: "Project",
29046
+ host: "Machine",
29047
+ localHost: "This machine",
28454
29048
  filterModel: "Filter by model",
28455
29049
  filterProject: "Filter by project",
28456
29050
  unknownModel: "Unknown model",
@@ -28634,6 +29228,8 @@ ${detail}` : ""}`
28634
29228
  harness: "Harness",
28635
29229
  model: "\u6A21\u578B",
28636
29230
  project: "\u9879\u76EE",
29231
+ host: "\u673A\u5668",
29232
+ localHost: "\u672C\u673A",
28637
29233
  filterModel: "\u6309\u6A21\u578B\u7B5B\u9009",
28638
29234
  filterProject: "\u6309\u9879\u76EE\u7B5B\u9009",
28639
29235
  unknownModel: "\u672A\u77E5\u6A21\u578B",
@@ -31103,7 +31699,7 @@ ${detail}` : ""}`
31103
31699
  }
31104
31700
  function defaultImportName(source, existing) {
31105
31701
  const base = source.provider === "openai-codex" ? "codex" : "grok";
31106
- const used = new Set(existing.map((record5) => record5.name));
31702
+ const used = new Set(existing.map((record6) => record6.name));
31107
31703
  const valid = (name) => credentialImportNameSchema.safeParse(name).success;
31108
31704
  if (!used.has(base)) return base;
31109
31705
  const local = source.label.includes("@") ? source.label.split("@")[0] ?? "" : "";
@@ -31126,7 +31722,7 @@ ${detail}` : ""}`
31126
31722
  const shown = (text) => accountDisplayText(text, hideEmails);
31127
31723
  const targetButtons = /* @__PURE__ */ new Map();
31128
31724
  const importedRecords = () => snapshot.targets.find((target) => target.harnessId === TARGET_HARNESS_ID)?.imports ?? [];
31129
- const sourceOf = (record5) => snapshot.sources.find((source) => source.id === record5.source.id);
31725
+ const sourceOf = (record6) => snapshot.sources.find((source) => source.id === record6.source.id);
31130
31726
  const piTarget = () => snapshot.targets.find((candidate) => candidate.harnessId === TARGET_HARNESS_ID);
31131
31727
  const compatibleSources = () => {
31132
31728
  const target = piTarget();
@@ -31164,7 +31760,7 @@ ${detail}` : ""}`
31164
31760
  sectionImport.addEventListener("click", () => {
31165
31761
  const sources = compatibleSources();
31166
31762
  const free = sources.find(
31167
- (source2) => !importedRecords().some((record5) => record5.source.id === source2.id)
31763
+ (source2) => !importedRecords().some((record6) => record6.source.id === source2.id)
31168
31764
  );
31169
31765
  const source = free ?? sources[0];
31170
31766
  if (source) openDialog("add", { source, choose: true }, SECTION_IMPORT_KEY);
@@ -31214,30 +31810,30 @@ ${detail}` : ""}`
31214
31810
  mark.append(createRendererAgentIcon(agent ?? "pi", 26, document2, pluginFor(agent ?? "pi")));
31215
31811
  return mark;
31216
31812
  };
31217
- const renderRow = (record5) => {
31813
+ const renderRow = (record6) => {
31218
31814
  const row = document2.createElement("div");
31219
31815
  row.className = "settings-pi-accounts__row";
31220
31816
  row.tabIndex = -1;
31221
- row.dataset.importName = record5.name;
31222
- row.setAttribute("aria-label", shown(record5.source.label));
31223
- const agent = record5.source.harnessId;
31817
+ row.dataset.importName = record6.name;
31818
+ row.setAttribute("aria-label", shown(record6.source.label));
31819
+ const agent = record6.source.harnessId;
31224
31820
  const mark = createMark(agent);
31225
31821
  const identity = document2.createElement("div");
31226
31822
  identity.className = "settings-pi-accounts__identity";
31227
31823
  const label = document2.createElement("strong");
31228
31824
  label.className = "settings-account-email";
31229
- label.textContent = shown(record5.source.label);
31825
+ label.textContent = shown(record6.source.label);
31230
31826
  label.title = label.textContent;
31231
31827
  label.translate = false;
31232
31828
  const meta3 = document2.createElement("div");
31233
31829
  meta3.className = "settings-account-metadata";
31234
- const source = sourceOf(record5);
31830
+ const source = sourceOf(record6);
31235
31831
  const parts = [
31236
31832
  metaPart(rendererAgentLabel(agent, pluginFor(agent)), "settings-pi-accounts__agent"),
31237
- metaPart(`${record5.name}/\u2026`, void 0, true),
31833
+ metaPart(`${record6.name}/\u2026`, void 0, true),
31238
31834
  metaPart(messages.copied)
31239
31835
  ];
31240
- const time3 = formatImportedAt(record5.importedAt);
31836
+ const time3 = formatImportedAt(record6.importedAt);
31241
31837
  if (time3) parts.push(metaPart(time3));
31242
31838
  parts.forEach((part, index) => {
31243
31839
  if (index > 0) {
@@ -31254,23 +31850,23 @@ ${detail}` : ""}`
31254
31850
  reimport.type = "button";
31255
31851
  reimport.className = "settings-account-action";
31256
31852
  reimport.textContent = messages.rowReimport;
31257
- reimport.setAttribute("aria-label", `${messages.reimport}: ${shown(record5.source.label)}`);
31853
+ reimport.setAttribute("aria-label", `${messages.reimport}: ${shown(record6.source.label)}`);
31258
31854
  reimport.disabled = busy || !source;
31259
31855
  reimport.title = messages.reimport;
31260
31856
  reimport.addEventListener("click", () => {
31261
- if (source) openDialog("reimport", { source, record: record5 }, `row:${record5.name}`);
31857
+ if (source) openDialog("reimport", { source, record: record6 }, `row:${record6.name}`);
31262
31858
  });
31263
31859
  const remove = document2.createElement("button");
31264
31860
  remove.type = "button";
31265
31861
  remove.className = "settings-account-action settings-pi-accounts__remove";
31266
31862
  remove.textContent = messages.rowRemove;
31267
- remove.setAttribute("aria-label", `${messages.remove}: ${shown(record5.source.label)}`);
31863
+ remove.setAttribute("aria-label", `${messages.remove}: ${shown(record6.source.label)}`);
31268
31864
  remove.disabled = busy;
31269
- remove.addEventListener("click", () => openDialog("remove", { record: record5 }, `row:${record5.name}`));
31865
+ remove.addEventListener("click", () => openDialog("remove", { record: record6 }, `row:${record6.name}`));
31270
31866
  if (source) actions.append(reimport);
31271
31867
  actions.append(remove);
31272
31868
  row.append(mark, identity, actions);
31273
- rows.set(record5.name, row);
31869
+ rows.set(record6.name, row);
31274
31870
  return row;
31275
31871
  };
31276
31872
  const loginTypeLabel = (type) => type === "oauth" ? messages.typeOauth : type === "api_key" ? messages.typeApiKey : messages.typeOther;
@@ -31423,7 +32019,7 @@ ${detail}` : ""}`
31423
32019
  const label = document2.createElement("strong");
31424
32020
  label.textContent = shown(choice.label);
31425
32021
  label.translate = false;
31426
- const existing = importedRecords().find((record5) => record5.source.id === choice.id);
32022
+ const existing = importedRecords().find((record6) => record6.source.id === choice.id);
31427
32023
  const agent = choice.harnessId;
31428
32024
  const detail = document2.createElement("span");
31429
32025
  detail.textContent = [
@@ -31464,7 +32060,7 @@ ${detail}` : ""}`
31464
32060
  const name = request.action === "list" ? void 0 : request.name;
31465
32061
  state = {
31466
32062
  source: state.source,
31467
- record: importedRecords().find((record5) => record5.name === name) ?? state.record
32063
+ record: importedRecords().find((record6) => record6.name === name) ?? state.record
31468
32064
  };
31469
32065
  show(next);
31470
32066
  } else {
@@ -31478,12 +32074,12 @@ ${detail}` : ""}`
31478
32074
  sourceRadios = [];
31479
32075
  body.replaceChildren();
31480
32076
  controls.replaceChildren();
31481
- const { source, record: record5 } = state;
32077
+ const { source, record: record6 } = state;
31482
32078
  const close = () => modal.close();
31483
32079
  if (mode === "add" || mode === "reimport") {
31484
32080
  title.textContent = mode === "add" ? messages.add : messages.reimport;
31485
32081
  modal.setAttribute("aria-label", title.textContent);
31486
- const active = source ?? record5?.source;
32082
+ const active = source ?? record6?.source;
31487
32083
  const choices = mode === "add" && state.choose ? compatibleSources() : [];
31488
32084
  if (choices.length > 1) body.append(sourcePicker(choices));
31489
32085
  else if (active) body.append(line(messages.source, shown(active.label)));
@@ -31492,8 +32088,8 @@ ${detail}` : ""}`
31492
32088
  input.maxLength = 48;
31493
32089
  input.autocomplete = "off";
31494
32090
  input.spellcheck = false;
31495
- input.value = record5?.name ?? (source ? defaultImportName(source, importedRecords()) : "codex");
31496
- input.readOnly = Boolean(record5);
32091
+ input.value = record6?.name ?? (source ? defaultImportName(source, importedRecords()) : "codex");
32092
+ input.readOnly = Boolean(record6);
31497
32093
  input.addEventListener("input", () => {
31498
32094
  nameEdited = true;
31499
32095
  });
@@ -31527,7 +32123,7 @@ ${detail}` : ""}`
31527
32123
  {
31528
32124
  action: mode === "reimport" ? "reimport" : "import",
31529
32125
  sourceId: source2.id,
31530
- name: record5?.name ?? input.value,
32126
+ name: record6?.name ?? input.value,
31531
32127
  confirmed: true
31532
32128
  },
31533
32129
  "done"
@@ -31537,16 +32133,16 @@ ${detail}` : ""}`
31537
32133
  )
31538
32134
  );
31539
32135
  (mode === "add" ? input : controls.children[0]).focus();
31540
- } else if (mode === "remove" && record5) {
32136
+ } else if (mode === "remove" && record6) {
31541
32137
  title.textContent = messages.remove;
31542
32138
  modal.setAttribute("aria-label", title.textContent);
31543
- body.append(note(messages.removeWarning.replace("{name}", record5.name)));
32139
+ body.append(note(messages.removeWarning.replace("{name}", record6.name)));
31544
32140
  const cancel = command(messages.cancel, close);
31545
32141
  controls.append(
31546
32142
  cancel,
31547
32143
  command(
31548
32144
  messages.remove,
31549
- () => submit({ action: "remove", name: record5.name, confirmed: true }, null),
32145
+ () => submit({ action: "remove", name: record6.name, confirmed: true }, null),
31550
32146
  "danger"
31551
32147
  )
31552
32148
  );
@@ -31554,7 +32150,7 @@ ${detail}` : ""}`
31554
32150
  } else {
31555
32151
  title.textContent = messages.doneTitle;
31556
32152
  modal.setAttribute("aria-label", title.textContent);
31557
- const name = record5?.name;
32153
+ const name = record6?.name;
31558
32154
  if (name) body.append(line(messages.entry, `${name}/\u2026`, true));
31559
32155
  body.append(note(messages.done));
31560
32156
  const done = command(messages.close, close, "primary");
@@ -31601,21 +32197,21 @@ ${detail}` : ""}`
31601
32197
  (candidate) => candidate.harnessId === TARGET_HARNESS_ID && source && candidate.providers.includes(source.provider)
31602
32198
  );
31603
32199
  if (!source || !target) return null;
31604
- const record5 = target.imports.find((candidate) => candidate.source.id === source.id);
32200
+ const record6 = target.imports.find((candidate) => candidate.source.id === source.id);
31605
32201
  const key = `${harnessId}:${accountLabel}:import`;
31606
32202
  const button2 = document2.createElement("button");
31607
32203
  button2.type = "button";
31608
32204
  button2.className = "settings-account-pi-import";
31609
32205
  button2.dataset.accountFocus = key;
31610
32206
  button2.disabled = busy;
31611
- const hint = record5 ? messages.importedHint.replace("{name}", record5.name) : messages.add;
32207
+ const hint = record6 ? messages.importedHint.replace("{name}", record6.name) : messages.add;
31612
32208
  button2.title = hint;
31613
32209
  button2.setAttribute("aria-label", hint);
31614
32210
  button2.append(createRendererAgentIcon("pi", 12, document2, pluginFor("pi")));
31615
- if (record5) {
32211
+ if (record6) {
31616
32212
  button2.dataset.state = "imported";
31617
32213
  const name = document2.createElement("code");
31618
- name.textContent = `${record5.name}/\u2026`;
32214
+ name.textContent = `${record6.name}/\u2026`;
31619
32215
  name.translate = false;
31620
32216
  button2.append(name, createRendererSettingsIcon("check", 12));
31621
32217
  } else {
@@ -31623,7 +32219,7 @@ ${detail}` : ""}`
31623
32219
  button2.append(messages.add);
31624
32220
  }
31625
32221
  button2.addEventListener("click", () => {
31626
- if (!record5) {
32222
+ if (!record6) {
31627
32223
  openDialog("add", { source }, key);
31628
32224
  return;
31629
32225
  }
@@ -31632,7 +32228,7 @@ ${detail}` : ""}`
31632
32228
  renderSection();
31633
32229
  sectionToggle.setAttribute("aria-expanded", "true");
31634
32230
  }
31635
- const row = rows.get(record5.name);
32231
+ const row = rows.get(record6.name);
31636
32232
  row?.scrollIntoView({ block: "nearest", behavior: "smooth" });
31637
32233
  row?.focus();
31638
32234
  });
@@ -33177,7 +33773,7 @@ ${detail}` : ""}`
33177
33773
  }
33178
33774
  });
33179
33775
  }
33180
- 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"))) {
33776
+ 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) {
33181
33777
  return Object.freeze([
33182
33778
  createConnectionsSettingsPage(messages, getDiagnostics, void 0, getRemoteConnections),
33183
33779
  createRemoteConnectionsPage(messages, getRemoteConnections),
@@ -33191,14 +33787,15 @@ ${detail}` : ""}`
33191
33787
  createUsageStatisticsPage(
33192
33788
  consoleMessages(messages.locale),
33193
33789
  requestUsageStatistics,
33194
- messages.locale
33790
+ messages.locale,
33791
+ usageStatisticsHosts
33195
33792
  ),
33196
33793
  createAppearanceSettingsPage(messages, getLoadedSessionsClient),
33197
33794
  updatesPage(messages, getUpdateClient),
33198
33795
  aboutPage(messages, getUpdateClient)
33199
33796
  ]);
33200
33797
  }
33201
- function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread, requestUsageStatistics) {
33798
+ function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread, requestUsageStatistics, usageStatisticsHosts) {
33202
33799
  return createRendererSettingsPageRegistry(
33203
33800
  createDefaultRendererSettingsPages(
33204
33801
  messages,
@@ -33209,7 +33806,8 @@ ${detail}` : ""}`
33209
33806
  openImportedThread,
33210
33807
  void 0,
33211
33808
  void 0,
33212
- requestUsageStatistics
33809
+ requestUsageStatistics,
33810
+ usageStatisticsHosts
33213
33811
  )
33214
33812
  );
33215
33813
  }
@@ -33983,6 +34581,20 @@ ${detail}` : ""}`
33983
34581
  gap: 18px;
33984
34582
  }
33985
34583
 
34584
+ .settings-connections-content {
34585
+ container: settings-connections / inline-size;
34586
+ }
34587
+
34588
+ @container settings-connections (max-width: 780px) {
34589
+ .settings-connections-layout {
34590
+ grid-template-columns: minmax(0, 1fr);
34591
+ }
34592
+
34593
+ .settings-connection-inspector {
34594
+ position: static;
34595
+ }
34596
+ }
34597
+
33986
34598
  .settings-connection-list,
33987
34599
  .settings-connection-inspector {
33988
34600
  overflow: hidden;
@@ -33991,14 +34603,36 @@ ${detail}` : ""}`
33991
34603
  border-radius: 10px;
33992
34604
  }
33993
34605
 
34606
+ .settings-connection-host-header {
34607
+ display: flex;
34608
+ align-items: center;
34609
+ min-width: 0;
34610
+ border-bottom: 1px solid var(--settings-divider);
34611
+ }
34612
+
34613
+ .settings-connection-update-all {
34614
+ flex: none;
34615
+ margin-right: 10px;
34616
+ white-space: nowrap;
34617
+ }
34618
+
34619
+ .settings-connection-update-result {
34620
+ margin: 0;
34621
+ padding: 7px 10px;
34622
+ color: var(--settings-muted);
34623
+ font-size: 11px;
34624
+ line-height: 18px;
34625
+ border-bottom: 1px solid var(--settings-divider);
34626
+ }
34627
+
33994
34628
  .settings-connection-host-strip {
33995
34629
  display: grid;
34630
+ flex: 1;
33996
34631
  grid-template-columns: 30px minmax(0, 1fr) 30px;
33997
34632
  align-items: center;
33998
34633
  min-width: 0;
33999
34634
  gap: 5px;
34000
34635
  padding: 9px 10px;
34001
- border-bottom: 1px solid var(--settings-divider);
34002
34636
  }
34003
34637
 
34004
34638
  .settings-connection-host-scroll {
@@ -34095,7 +34729,7 @@ ${detail}` : ""}`
34095
34729
  .settings-connection-table-header,
34096
34730
  .settings-connection-row {
34097
34731
  display: grid;
34098
- grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 108px;
34732
+ grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) minmax(108px, max-content);
34099
34733
  align-items: center;
34100
34734
  min-width: 0;
34101
34735
  gap: 10px;
@@ -34188,6 +34822,14 @@ ${detail}` : ""}`
34188
34822
  line-height: 18px;
34189
34823
  }
34190
34824
 
34825
+ .settings-connection-version-available {
34826
+ flex: none;
34827
+ color: var(--settings-muted);
34828
+ font-size: 10px;
34829
+ line-height: 16px;
34830
+ white-space: nowrap;
34831
+ }
34832
+
34191
34833
  .settings-connection-row__status::before {
34192
34834
  width: 6px;
34193
34835
  height: 6px;
@@ -34224,6 +34866,7 @@ ${detail}` : ""}`
34224
34866
  justify-content: flex-end;
34225
34867
  min-width: 0;
34226
34868
  gap: 4px;
34869
+ white-space: nowrap;
34227
34870
  }
34228
34871
 
34229
34872
  .settings-connection-row__handle {
@@ -38381,7 +39024,8 @@ ${usage_default}`;
38381
39024
  },
38382
39025
  options.getLoadedSessionsClient ?? (() => null),
38383
39026
  options.getRemoteConnections ?? (() => null),
38384
- options.requestUsageStatistics
39027
+ options.requestUsageStatistics,
39028
+ options.usageStatisticsHosts
38385
39029
  );
38386
39030
  const nextShell = installRendererSettingsShell(definitions, messages, ownerWindow.document, {
38387
39031
  onOpenChange(open) {
@@ -38530,6 +39174,30 @@ ${usage_default}`;
38530
39174
  };
38531
39175
  }
38532
39176
 
39177
+ // src/renderer-usage-statistics-source.ts
39178
+ function createRendererUsageStatisticsSource(options) {
39179
+ return {
39180
+ async request(method, params, hostId2) {
39181
+ const route = options.routing()?.forHost(hostId2);
39182
+ if (options.disposed() || !route) {
39183
+ throw new Error(`Host connection is unavailable (${hostId2})`);
39184
+ }
39185
+ return route.manager.sendRequest(method, params);
39186
+ },
39187
+ async hosts() {
39188
+ const connections = await options.listConnections().catch(() => []);
39189
+ const names = new Map(
39190
+ connections.map((connection) => [connection.hostId, connection.displayName])
39191
+ );
39192
+ const known = options.routing()?.knownHostIds?.() ?? [];
39193
+ return [.../* @__PURE__ */ new Set([...known, ...names.keys()])].map((hostId2) => {
39194
+ const label = names.get(hostId2);
39195
+ return label === void 0 ? { hostId: hostId2 } : { hostId: hostId2, label };
39196
+ });
39197
+ }
39198
+ };
39199
+ }
39200
+
38533
39201
  // src/renderer-native-composer-controller.ts
38534
39202
  var MAX_ELEMENT_DEPTH = 4;
38535
39203
  var MAX_FIBER_DEPTH = 40;
@@ -39458,17 +40126,19 @@ ${usage_default}`;
39458
40126
  });
39459
40127
  let connectionDiagnostics = null;
39460
40128
  const remoteConnections = createRemoteConnectionsControl(window, modelClientForHost);
40129
+ const usageStatistics = createRendererUsageStatisticsSource({
40130
+ routing: () => window.__codexhostHostRoutingV1,
40131
+ listConnections: () => remoteConnections.ssh.list(),
40132
+ disposed: () => disposed
40133
+ });
39461
40134
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
39462
40135
  getRemoteConnections: () => remoteConnections,
39463
40136
  getUpdateClient: () => modelControl,
39464
40137
  getAccountClient: () => modelControl,
39465
40138
  getConnectionDiagnostics: () => connectionDiagnostics,
39466
40139
  getLoadedSessionsClient: () => modelClientForHost("local"),
39467
- async requestUsageStatistics(method, params) {
39468
- const route = window.__codexhostHostRoutingV1?.forHost("local");
39469
- if (disposed || !route) throw new Error("Local Host connection is unavailable");
39470
- return route.manager.sendRequest(method, params);
39471
- },
40140
+ requestUsageStatistics: usageStatistics.request,
40141
+ usageStatisticsHosts: usageStatistics.hosts,
39472
40142
  getSessionImportClient: () => {
39473
40143
  const client = modelClientForHost("local");
39474
40144
  const sources = client?.listSessionImportSources;
@@ -39565,9 +40235,10 @@ ${usage_default}`;
39565
40235
  };
39566
40236
  const notifySubmission = (composer, trigger) => {
39567
40237
  const state = controller.recordSubmission(composer);
40238
+ const createsThread = state.phase === "draft";
39568
40239
  const preferenceStorage = rendererNewThreadPreferenceStorage(activeModelHostId(composer));
39569
- writeNewThreadAgentPreference(state.agent, preferenceStorage);
39570
- if (state.agent !== "codex") {
40240
+ if (createsThread) writeNewThreadAgentPreference(state.agent, preferenceStorage);
40241
+ if (createsThread && state.agent !== "codex") {
39571
40242
  const model2 = controller.modelForAgent(composer, state.agent);
39572
40243
  if (model2) {
39573
40244
  writeNewThreadExternalConfigurationPreference(
@@ -40706,7 +41377,7 @@ ${usage_default}`;
40706
41377
  let webUiAvailable = false;
40707
41378
  try {
40708
41379
  const inspection = await client.inspectHarness(
40709
- { harnessId: harnessIdSchema.parse(agent), refresh },
41380
+ { harnessId: harnessIdSchema.parse(agent), refresh, purpose: "availability" },
40710
41381
  { priority: "background" }
40711
41382
  );
40712
41383
  status = inspection.status === "ready" ? "ready" : inspection.status;