@codexhost/cli-linux-arm64 0.13.2 → 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.
Files changed (33) hide show
  1. package/README.md +1 -1
  2. package/app/codexhost-distribution.json +1 -1
  3. package/app/console-server.mjs +94 -8
  4. package/app/console-web.js +537 -146
  5. package/app/desktop-controller.mjs +108 -12
  6. package/app/host-runtime.mjs +847 -235
  7. package/app/plugins/antigravity/plugin.mjs +143 -20
  8. package/app/plugins/claude-code/plugin.mjs +601 -71
  9. package/app/plugins/codebuddy/plugin.mjs +227 -30
  10. package/app/plugins/codex-usage/plugin.mjs +212 -77
  11. package/app/plugins/copilot/assets/icon.png +0 -0
  12. package/app/plugins/copilot/manifest.json +26 -0
  13. package/app/plugins/copilot/plugin.mjs +34546 -0
  14. package/app/plugins/cursor-cli/plugin.mjs +20805 -44862
  15. package/app/plugins/deepseek-harness/manifest.json +2 -2
  16. package/app/plugins/deepseek-harness/plugin.mjs +202 -21
  17. package/app/plugins/enabled.json +1 -0
  18. package/app/plugins/grok/plugin.mjs +278 -29
  19. package/app/plugins/hermes/plugin.mjs +143 -12
  20. package/app/plugins/kimi-code/plugin.mjs +140 -16
  21. package/app/plugins/kiro-cli/plugin.mjs +121 -10
  22. package/app/plugins/omp/plugin.mjs +2200 -2002
  23. package/app/plugins/opencode/plugin.mjs +137 -16
  24. package/app/plugins/pi/plugin.mjs +2563 -2282
  25. package/app/plugins/qoder/plugin.mjs +133 -14
  26. package/app/plugins/qoder-cn/plugin.mjs +133 -14
  27. package/app/plugins/workbuddy/plugin.mjs +603 -56
  28. package/app/plugins/zcode/plugin.mjs +133 -14
  29. package/app/renderer-extension.js +1171 -529
  30. package/bin/codexhost +0 -0
  31. package/libexec/codexhost-shim +0 -0
  32. package/libexec/codexhost-updater +0 -0
  33. package/package.json +1 -1
@@ -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) {
@@ -14835,6 +14836,48 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14835
14836
  initializeOnly: external_exports.boolean().optional()
14836
14837
  });
14837
14838
 
14839
+ // ../shared-contracts/dist/harness-presentation.js
14840
+ var localeSchema = external_exports.string().max(64).regex(/^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/u);
14841
+ var displayText = (maximum) => external_exports.string().min(1).max(maximum).refine((text) => text.trim().length > 0);
14842
+ var harnessLocalizationsSchema = external_exports.record(localeSchema, external_exports.object({
14843
+ label: displayText(256).optional(),
14844
+ description: displayText(1024).optional()
14845
+ }).strict()).refine((entries) => Object.keys(entries).length <= 16, "Too many locales");
14846
+ var harnessLocalizedTextSchema = external_exports.record(localeSchema, displayText(4096)).refine((entries) => typeof entries.en === "string", "English fallback is required").refine((entries) => Object.keys(entries).length <= 16, "Too many locales");
14847
+ function translation(entries, locale) {
14848
+ if (!entries)
14849
+ return void 0;
14850
+ const language = locale.split("-")[0] ?? locale;
14851
+ for (const candidate of [locale, language]) {
14852
+ if (Object.hasOwn(entries, candidate))
14853
+ return entries[candidate];
14854
+ }
14855
+ return void 0;
14856
+ }
14857
+ function localizeHarnessPresentation(value, locale) {
14858
+ const localized = translation(value.localizations, locale);
14859
+ const language = translation(value.localizations, locale.split("-")[0] ?? locale);
14860
+ return {
14861
+ label: localized?.label ?? language?.label ?? value.label,
14862
+ description: localized?.description ?? language?.description ?? value.description
14863
+ };
14864
+ }
14865
+ function localizeHarnessText(value, locale) {
14866
+ return translation(value, locale) ?? value.en ?? "";
14867
+ }
14868
+
14869
+ // ../shared-contracts/dist/account-credits-presentation.js
14870
+ function accountCreditsWindowLabel(window2, locale, periodLabel) {
14871
+ if (window2.periodType && window2.periodType !== "unknown" && (window2.scope || !window2.label)) {
14872
+ const period = periodLabel(window2.periodType);
14873
+ return window2.scope ? `${localizeHarnessPresentation(window2.scope, locale).label} \xB7 ${period}` : period;
14874
+ }
14875
+ return localizeHarnessPresentation({
14876
+ label: window2.label ?? window2.product ?? periodLabel("unknown"),
14877
+ ...window2.localizations ? { localizations: window2.localizations } : {}
14878
+ }, locale).label;
14879
+ }
14880
+
14838
14881
  // ../shared-contracts/dist/model-price-overrides.js
14839
14882
  var MODEL_PRICE_OVERRIDES_GET_METHOD = "codexhost/usage/model-prices/get";
14840
14883
  var MODEL_PRICE_OVERRIDES_SET_METHOD = "codexhost/usage/model-prices/set";
@@ -14888,10 +14931,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14888
14931
  var project = external_exports.string().min(1).max(4096);
14889
14932
  var usageStatisticsParamsSchema = external_exports.strictObject({
14890
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(),
14891
14943
  harness: harness.optional(),
14892
14944
  model: model.nullable().optional(),
14893
14945
  project: project.nullable().optional(),
14894
- /** One local date inside the range. */
14946
+ /** One date in the query timezone inside the range. */
14895
14947
  date: localDate.optional()
14896
14948
  });
14897
14949
  var usageStatisticsTotalsSchema = external_exports.strictObject({
@@ -14927,7 +14979,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14927
14979
  });
14928
14980
  var usageStatisticsResultSchema = external_exports.strictObject({
14929
14981
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
14930
- /** 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. */
14931
14983
  from: localDate.nullable(),
14932
14984
  to: localDate,
14933
14985
  /** The filters these figures apply, echoed so a late answer to older filters is recognised. */
@@ -14967,7 +15019,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14967
15019
  totals: usageStatisticsTotalsSchema,
14968
15020
  /** Each date and Harness of the range: every filter except the day. */
14969
15021
  daily: external_exports.array(external_exports.strictObject({ date: localDate, harness, ...totals })).max(2e5),
14970
- /** Local weekday (0 = Sunday) and hour, all filters applied. */
15022
+ /** Weekday (0 = Sunday) and hour in the query timezone, all filters applied. */
14971
15023
  hourly: external_exports.array(external_exports.strictObject({
14972
15024
  weekday: external_exports.number().int().min(0).max(6),
14973
15025
  hour: external_exports.number().int().min(0).max(23),
@@ -15028,6 +15080,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15028
15080
  "codexhost/harness/display-settings/set",
15029
15081
  "codexhost/harness/inspect",
15030
15082
  "codexhost/harness/installation",
15083
+ "codexhost/harness/installation/start",
15084
+ "codexhost/harness/installation/status",
15031
15085
  "codexhost/harness/launch-settings/get",
15032
15086
  "codexhost/harness/launch-settings/set",
15033
15087
  "codexhost/harness/web-ui/open",
@@ -15106,7 +15160,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15106
15160
  var nonNegativeSafeIntegerSchema = external_exports.number().int().safe().nonnegative();
15107
15161
  var finiteNonNegativeNumberSchema = external_exports.number().finite().nonnegative();
15108
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
+ });
15109
15173
  var threadUsageSnapshotSchema = external_exports.object({
15174
+ composerIndicators: composerIndicatorsSchema.optional(),
15110
15175
  inputTokens: nonNegativeSafeIntegerSchema.optional(),
15111
15176
  cachedInputTokens: nonNegativeSafeIntegerSchema.optional(),
15112
15177
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
@@ -15179,8 +15244,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15179
15244
  }
15180
15245
  });
15181
15246
  var usagePercentSchema = external_exports.number().finite().min(0).max(100);
15247
+ var accountCreditsPeriodSchema = external_exports.enum([
15248
+ "weekly",
15249
+ "monthly",
15250
+ "five_hour",
15251
+ "seven_day",
15252
+ "unknown"
15253
+ ]);
15254
+ var accountCreditsScopeSchema = external_exports.object({
15255
+ id: external_exports.string().min(1).max(256),
15256
+ label: external_exports.string().min(1).max(256),
15257
+ localizations: harnessLocalizationsSchema.optional()
15258
+ }).strict();
15182
15259
  var accountCreditsProductUsageSchema = external_exports.object({
15183
15260
  product: external_exports.string().min(1),
15261
+ /** Native period and scope, never inferred from the display name. */
15262
+ periodType: accountCreditsPeriodSchema.optional(),
15263
+ scope: accountCreditsScopeSchema.optional(),
15264
+ localizations: harnessLocalizationsSchema.optional(),
15184
15265
  used: external_exports.number().finite().nonnegative().optional(),
15185
15266
  // Zero is a real empty cap, not an absent field.
15186
15267
  limit: external_exports.number().finite().nonnegative().optional(),
@@ -15200,9 +15281,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15200
15281
  unit: external_exports.string().min(1).max(32).optional(),
15201
15282
  /** Native label when the primary limit is scoped to a model or product group. */
15202
15283
  label: external_exports.string().min(1).optional(),
15284
+ scope: accountCreditsScopeSchema.optional(),
15285
+ localizations: harnessLocalizationsSchema.optional(),
15203
15286
  usedPercent: usagePercentSchema,
15204
15287
  resetsAt: external_exports.string().min(1).optional(),
15205
- periodType: external_exports.enum(["weekly", "monthly", "five_hour", "seven_day", "unknown"]),
15288
+ periodType: accountCreditsPeriodSchema,
15206
15289
  productUsage: external_exports.array(accountCreditsProductUsageSchema).min(1).optional(),
15207
15290
  resetCredits: accountResetCreditsSchema.optional()
15208
15291
  }).strict();
@@ -15213,7 +15296,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15213
15296
  var threadUsageInspectionSchema = external_exports.object({
15214
15297
  threadId: hostThreadIdSchema,
15215
15298
  usage: threadUsageSnapshotSchema.nullable(),
15216
- accountCredits: accountCreditsSnapshotSchema.optional()
15299
+ accountCredits: accountCreditsSnapshotSchema.optional(),
15300
+ /** Whether this source can provide cached account quota independently of usage. */
15301
+ accountCreditsSupported: external_exports.boolean().optional()
15217
15302
  }).strict();
15218
15303
 
15219
15304
  // ../shared-contracts/dist/harness-accounts.js
@@ -15340,7 +15425,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15340
15425
  id: harnessPermissionModeIdSchema,
15341
15426
  label: nonBlankTextSchema.max(HARNESS_PERMISSION_MODE_LABEL_MAX_LENGTH),
15342
15427
  description: nonBlankTextSchema.max(HARNESS_PERMISSION_MODE_DESCRIPTION_MAX_LENGTH).optional(),
15343
- dangerous: external_exports.boolean().optional()
15428
+ dangerous: external_exports.boolean().optional(),
15429
+ localizations: harnessLocalizationsSchema.optional()
15344
15430
  }).strict();
15345
15431
  var harnessPermissionModeCatalogSchema = external_exports.object({
15346
15432
  modes: external_exports.array(harnessPermissionModeSchema).min(1).max(HARNESS_PERMISSION_MODE_CATALOG_MAX_LENGTH),
@@ -15501,7 +15587,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15501
15587
  }).strict().optional(),
15502
15588
  autonomousTurns: external_exports.object({
15503
15589
  observe: external_exports.boolean()
15504
- }).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()
15505
15593
  }).strict();
15506
15594
  var harnessConfigurationStateSchema = external_exports.object({
15507
15595
  effectiveModel: harnessModelRefSchema.optional(),
@@ -15549,7 +15637,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15549
15637
  var harnessInspectParamsSchema = external_exports.object({
15550
15638
  harnessId: harnessIdSchema,
15551
15639
  cwd: nonBlankTextSchema2.max(16384).optional(),
15552
- 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()
15553
15643
  }).strict();
15554
15644
  var harnessWebUiOpenParamsSchema = external_exports.object({
15555
15645
  harnessId: harnessIdSchema
@@ -15632,6 +15722,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15632
15722
  seen.add(thread.threadId);
15633
15723
  }
15634
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();
15635
15735
 
15636
15736
  // ../shared-contracts/dist/harness-plugins.js
15637
15737
  var HARNESS_PLUGIN_MANIFEST_MAX_BYTES = 32 * 1024;
@@ -15750,7 +15850,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15750
15850
  }).strict();
15751
15851
 
15752
15852
  // ../shared-contracts/dist/harness-installation.js
15753
- 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";
15754
15855
  var harnessInstallationParamsSchema = external_exports.object({
15755
15856
  harnessId: harnessIdSchema,
15756
15857
  action: external_exports.enum(["check", "update", "install"])
@@ -15760,11 +15861,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15760
15861
  latestVersion: external_exports.string().min(1).max(128),
15761
15862
  updateAvailable: external_exports.boolean(),
15762
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(),
15763
15868
  // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
15764
15869
  message: external_exports.string().max(2048).optional(),
15765
15870
  messageCode: external_exports.string().min(1).max(128).optional(),
15871
+ /** Safe plugin-owned explanation, distinct from raw diagnostic message. */
15872
+ notice: harnessLocalizedTextSchema.optional(),
15766
15873
  latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
15767
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
+ ]);
15768
15889
 
15769
15890
  // ../shared-contracts/dist/codex-accounts.js
15770
15891
  var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
@@ -15894,6 +16015,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15894
16015
  invocation: commandInvocationSchema,
15895
16016
  label: commandLabelSchema,
15896
16017
  description: commandDescriptionSchema.optional(),
16018
+ localizations: harnessLocalizationsSchema.optional(),
15897
16019
  argumentMode: external_exports.enum(["none", "text"]),
15898
16020
  /**
15899
16021
  * Native distinction reported by the Harness. Omitted when the Harness does
@@ -16174,9 +16296,63 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16174
16296
  // ../shared-contracts/dist/index.js
16175
16297
  var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
16176
16298
 
16177
- // 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
16178
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);
16179
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
+ }
16180
16356
  function request(operation, params, signal) {
16181
16357
  const bridge = ownerWindow.electronBridge;
16182
16358
  if (!bridge?.sendMessageFromView)
@@ -16184,7 +16360,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16184
16360
  if (signal?.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
16185
16361
  return new Promise((resolve, reject) => {
16186
16362
  const requestId = ownerWindow.crypto.randomUUID();
16363
+ let settled = false;
16187
16364
  const finish = (error51, value) => {
16365
+ if (settled) return;
16366
+ settled = true;
16188
16367
  ownerWindow.clearTimeout(timer);
16189
16368
  ownerWindow.removeEventListener("message", receive);
16190
16369
  signal?.removeEventListener("abort", abort);
@@ -16194,10 +16373,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16194
16373
  const abort = () => finish(new DOMException("Aborted", "AbortError"));
16195
16374
  const receive = (event) => {
16196
16375
  const message2 = event.data;
16197
- if (!record2(message2) || message2.type !== "fetch-response" || message2.requestId !== requestId)
16376
+ if (!record3(message2) || message2.type !== "fetch-response" || message2.requestId !== requestId)
16198
16377
  return;
16199
16378
  if (message2.responseType !== "success" || typeof message2.status !== "number" || message2.status < 200 || message2.status >= 300 || typeof message2.bodyJsonString !== "string") {
16200
- 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
+ );
16201
16383
  return;
16202
16384
  }
16203
16385
  try {
@@ -16212,30 +16394,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16212
16394
  );
16213
16395
  ownerWindow.addEventListener("message", receive);
16214
16396
  signal?.addEventListener("abort", abort, { once: true });
16215
- try {
16216
- Promise.resolve(
16217
- 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({
16218
16419
  type: "fetch",
16219
16420
  requestId,
16220
16421
  method: "POST",
16221
16422
  url: `vscode://codex/${operation}`,
16222
16423
  body: JSON.stringify(params),
16223
16424
  reportUploadProgress: false
16224
- })
16225
- ).catch((error51) => finish(error51));
16226
- } catch (error51) {
16227
- finish(error51);
16228
- }
16425
+ });
16426
+ }
16427
+ })().catch((error51) => finish(error51));
16229
16428
  });
16230
16429
  }
16231
16430
  async function list(signal) {
16232
16431
  const result = await request("refresh-remote-connections", {}, signal);
16233
- if (!record2(result) || !Array.isArray(result.remoteConnections))
16432
+ if (!record3(result) || !Array.isArray(result.remoteConnections))
16234
16433
  throw new Error("Invalid Codex SSH connection list");
16235
16434
  return result.remoteConnections.filter(
16236
- (item) => !(record2(item) && ["wsl", "remote-control"].includes(String(item.source)))
16435
+ (item) => !(record3(item) && ["wsl", "remote-control"].includes(String(item.source)))
16237
16436
  ).map((item) => {
16238
- 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)) {
16239
16438
  throw new Error(
16240
16439
  "This Codex version uses an unsupported SSH configuration; open native settings"
16241
16440
  );
@@ -16307,11 +16506,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16307
16506
  },
16308
16507
  async state(hostId2, signal) {
16309
16508
  const result = await request("app-server-connection-state", { hostId: hostId2 }, signal);
16310
- if (!record2(result) || typeof result.state !== "string")
16509
+ if (!record3(result) || typeof result.state !== "string")
16311
16510
  throw new Error("Invalid Codex connection state");
16312
16511
  return {
16313
16512
  state: result.state,
16314
- 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
16315
16514
  };
16316
16515
  }
16317
16516
  };
@@ -17457,37 +17656,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17457
17656
 
17458
17657
  // src/settings/harness-version-messages.ts
17459
17658
  var harnessVersionEnglish = Object.freeze({
17659
+ locale: "en",
17460
17660
  unknown: "Unknown",
17461
17661
  trackingBranch: "Tracking branch (new commits)",
17462
17662
  notes: Object.freeze({
17463
- "original-installer": "Use the original installer, package manager, or launcher to update this installation.",
17464
- "claude-policy-restricted": "Claude updates are restricted by the installation's settings or policy.",
17465
- "codebuddy-npm-required": "Use CodeBuddy's native updater or original package manager. Latest-version checks here require an identified npm installation.",
17466
- "deepseek-original-installer": "Update with the original installer. Python, desktop, and project-local installations are not upgraded through global npm.",
17467
- "kiro-native-updater": "Use Kiro CLI's original installer or native updater to check for and install updates.",
17468
- "zcode-desktop-updater": "The CLI ships inside ZCode Desktop. Update ZCode Desktop itself to update it.",
17469
- "qoder-check-unavailable": "This Qoder China release does not expose a non-installing update check. Use its original installer.",
17470
- "hermes-update-plan-unavailable": "This Hermes release does not expose a safe update plan. Use the original installer.",
17471
- "hermes-externally-managed": "This Hermes installation is managed externally. Update it through its desktop app, container, or original package manager.",
17472
- "hermes-update-channel": "Uses Hermes's configured update channel. Gateway restarts are deferred; existing processes keep their running code.",
17473
- "hermes-manual-update": "Use the native updater manually. Safe non-interactive updates require a clean source checkout, commit identity, and Gateway restart deferral."
17663
+ "original-installer": "Use the original installer, package manager, or launcher to update this installation."
17474
17664
  })
17475
17665
  });
17476
17666
  var harnessVersionChinese = Object.freeze({
17667
+ locale: "zh-CN",
17477
17668
  unknown: "\u672A\u77E5",
17478
17669
  trackingBranch: "\u8DDF\u8E2A\u5206\u652F\uFF08\u6709\u65B0\u63D0\u4EA4\uFF09",
17479
17670
  notes: Object.freeze({
17480
- "original-installer": "\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u7A0B\u5E8F\u3001\u5305\u7BA1\u7406\u5668\u6216\u542F\u52A8\u5668\u66F4\u65B0\u6B64\u5B89\u88C5\u3002",
17481
- "claude-policy-restricted": "\u6B64 Claude \u5B89\u88C5\u7684\u8BBE\u7F6E\u6216\u7B56\u7565\u9650\u5236\u4E86\u66F4\u65B0\u3002",
17482
- "codebuddy-npm-required": "\u8BF7\u4F7F\u7528 CodeBuddy \u539F\u751F\u66F4\u65B0\u5668\u6216\u539F\u5305\u7BA1\u7406\u5668\u66F4\u65B0\u3002\u6B64\u5904\u4EC5\u80FD\u67E5\u8BE2\u5DF2\u8BC6\u522B\u7684 npm \u5B89\u88C5\u7684\u6700\u65B0\u7248\u672C\u3002",
17483
- "deepseek-original-installer": "\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002Python\u3001\u684C\u9762\u5E94\u7528\u53CA\u9879\u76EE\u5185\u5B89\u88C5\u4E0D\u4F1A\u901A\u8FC7\u5168\u5C40 npm \u66F4\u65B0\u3002",
17484
- "kiro-native-updater": "\u8BF7\u4F7F\u7528 Kiro CLI \u539F\u5B89\u88C5\u7A0B\u5E8F\u6216\u539F\u751F\u66F4\u65B0\u5668\u68C0\u67E5\u5E76\u5B89\u88C5\u66F4\u65B0\u3002",
17485
- "zcode-desktop-updater": "CLI \u968F ZCode Desktop \u4E00\u8D77\u53D1\u5E03\uFF0C\u8BF7\u76F4\u63A5\u66F4\u65B0 ZCode Desktop\u3002",
17486
- "qoder-check-unavailable": "\u6B64 Qoder \u4E2D\u56FD\u7248\u5C1A\u4E0D\u652F\u6301\u53EA\u68C0\u67E5\u3001\u4E0D\u5B89\u88C5\u7684\u66F4\u65B0\u67E5\u8BE2\uFF0C\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002",
17487
- "hermes-update-plan-unavailable": "\u6B64 Hermes \u7248\u672C\u4E0D\u63D0\u4F9B\u5B89\u5168\u7684\u66F4\u65B0\u8BA1\u5212\uFF0C\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002",
17488
- "hermes-externally-managed": "\u6B64 Hermes \u5B89\u88C5\u7531\u5916\u90E8\u5DE5\u5177\u7BA1\u7406\uFF0C\u8BF7\u901A\u8FC7\u5176\u684C\u9762\u5E94\u7528\u3001\u5BB9\u5668\u6216\u539F\u5305\u7BA1\u7406\u5668\u66F4\u65B0\u3002",
17489
- "hermes-update-channel": "\u4F7F\u7528 Hermes \u914D\u7F6E\u7684\u66F4\u65B0\u6E20\u9053\uFF0C\u6682\u4E0D\u91CD\u542F Gateway\uFF1B\u73B0\u6709\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\u539F\u6709\u4EE3\u7801\u3002",
17490
- "hermes-manual-update": "\u8BF7\u624B\u52A8\u4F7F\u7528\u539F\u751F\u66F4\u65B0\u5668\u3002\u5B89\u5168\u7684\u975E\u4EA4\u4E92\u66F4\u65B0\u8981\u6C42\u6E90\u7801\u76EE\u5F55\u65E0\u672A\u63D0\u4EA4\u4FEE\u6539\u3001\u53EF\u8BC6\u522B\u5F53\u524D\u63D0\u4EA4\uFF0C\u5E76\u652F\u6301\u63A8\u8FDF Gateway \u91CD\u542F\u3002"
17671
+ "original-installer": "\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u7A0B\u5E8F\u3001\u5305\u7BA1\u7406\u5668\u6216\u542F\u52A8\u5668\u66F4\u65B0\u6B64\u5B89\u88C5\u3002"
17491
17672
  })
17492
17673
  });
17493
17674
  function harnessVersionLabel(state, messages) {
@@ -17499,12 +17680,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17499
17680
  return state.latestVersion;
17500
17681
  }
17501
17682
  function harnessVersionNote(state, messages) {
17683
+ if (state?.notice) return localizeHarnessText(state.notice, messages.locale);
17502
17684
  const code = state?.messageCode;
17503
17685
  return code && Object.hasOwn(messages.notes, code) ? messages.notes[code] : void 0;
17504
17686
  }
17505
17687
 
17506
17688
  // src/settings/harness-version-panel.ts
17507
- function createHarnessVersionPanel(document2, messages, signal, agent, client) {
17689
+ function createHarnessVersionPanel(document2, messages, signal, agent, client, onChange = () => void 0) {
17508
17690
  const panel = document2.createElement("section");
17509
17691
  panel.className = "settings-harness-version";
17510
17692
  panel.dataset.harnessVersion = agent;
@@ -17530,37 +17712,67 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17530
17712
  panel.append(heading, versions, status, actions, note);
17531
17713
  let state;
17532
17714
  let busy = false;
17715
+ let updating = false;
17533
17716
  let unsupported = false;
17717
+ let updatePromise = null;
17534
17718
  const render = () => {
17719
+ heading.textContent = state?.versionKind === "application" ? messages.harnessVersionApplicationTitle : messages.harnessVersionTitle;
17535
17720
  current.textContent = `${messages.harnessVersionCurrent}: ${state?.currentVersion ?? "\u2014"}`;
17536
17721
  latest.textContent = `${messages.harnessVersionLatest}: ${harnessVersionLabel(state, messages.harnessVersion)}`;
17537
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
+ }
17538
17736
  update.textContent = state?.canUpdate && state.latestVersion !== "Unknown" && !state.updateAvailable ? messages.harnessVersionUpToDate : messages.harnessVersionUpdate;
17539
17737
  const detail = harnessVersionNote(state, messages.harnessVersion);
17540
- note.textContent = detail ? `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
17738
+ note.textContent = detail ? !state?.canUpdate ? detail : `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
17541
17739
  };
17542
17740
  const run = async (action) => {
17543
- if (busy || unsupported || signal.aborted) return;
17544
- 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;
17545
17743
  busy = true;
17744
+ updating = action === "update";
17745
+ onChange();
17546
17746
  status.textContent = action === "check" ? messages.harnessVersionChecking : messages.harnessVersionUpdating;
17547
17747
  render();
17548
17748
  try {
17549
17749
  const result = await client.run(action);
17550
- if (signal.aborted) return;
17750
+ if (signal.aborted) return false;
17551
17751
  state = result;
17552
17752
  status.textContent = !result.canUpdate ? messages.harnessVersionManual : action === "update" ? result.updateAvailable ? messages.harnessVersionFailed : messages.harnessVersionUpdated : "";
17753
+ return result.canUpdate && !result.updateAvailable;
17553
17754
  } catch (error51) {
17554
- if (signal.aborted) return;
17755
+ if (signal.aborted) return false;
17555
17756
  unsupported = typeof error51 === "object" && error51 !== null && "code" in error51 && (error51.code === -32601 || error51.code === -32078);
17556
17757
  status.textContent = unsupported ? messages.harnessVersionUnsupported : messages.harnessVersionFailed;
17557
17758
  if (state) state = { ...state, canUpdate: false };
17759
+ return false;
17558
17760
  } finally {
17559
17761
  busy = false;
17560
- if (!signal.aborted) render();
17762
+ updating = false;
17763
+ if (!signal.aborted) {
17764
+ render();
17765
+ onChange();
17766
+ }
17561
17767
  }
17562
17768
  };
17563
- 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());
17564
17776
  const owner = document2.defaultView;
17565
17777
  let checkFrame = null;
17566
17778
  let checkTimer = null;
@@ -17587,7 +17799,95 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17587
17799
  } else {
17588
17800
  void run("check");
17589
17801
  }
17590
- 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
+ };
17591
17891
  }
17592
17892
 
17593
17893
  // src/settings/connections-page.ts
@@ -17755,6 +18055,7 @@ ${error51.stderrTail}`] : []
17755
18055
  status.dataset.connectionTone = connectionStatusTone(item.availability, item.error !== null);
17756
18056
  status.setAttribute("role", "cell");
17757
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;
17758
18059
  const action = document2.createElement("div");
17759
18060
  action.className = "settings-connection-row__action";
17760
18061
  action.setAttribute("role", "cell");
@@ -17784,6 +18085,31 @@ ${error51.stderrTail}`] : []
17784
18085
  });
17785
18086
  action.append(viewError);
17786
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
+ }
17787
18113
  if (group) {
17788
18114
  const toggle = document2.createElement("button");
17789
18115
  toggle.type = "button";
@@ -18075,8 +18401,6 @@ ${error51.stderrTail}`] : []
18075
18401
  let selectedHostId = null;
18076
18402
  let selectedItemKey = null;
18077
18403
  let launchControls = null;
18078
- const versionPanels = /* @__PURE__ */ new Map();
18079
- const updatingVersions = /* @__PURE__ */ new Set();
18080
18404
  const hostNames = /* @__PURE__ */ new Map();
18081
18405
  let latestSnapshot = null;
18082
18406
  let disposeHostScroller = () => void 0;
@@ -18105,7 +18429,15 @@ ${error51.stderrTail}`] : []
18105
18429
  renderFrame = null;
18106
18430
  };
18107
18431
  const diagnostics = getDiagnostics();
18432
+ const versions = createConnectionVersions(
18433
+ document2,
18434
+ messages,
18435
+ context.signal,
18436
+ diagnostics,
18437
+ scheduleRender
18438
+ );
18108
18439
  const installs = diagnostics ? harnessInstallStore(diagnostics) : null;
18440
+ const updates = createConnectionUpdates(document2, messages, context.signal, scheduleRender);
18109
18441
  const runRefresh = () => {
18110
18442
  if (pending || !diagnostics) return;
18111
18443
  pending = true;
@@ -18241,10 +18573,13 @@ ${error51.stderrTail}`] : []
18241
18573
  const agent = item.agentSnapshot?.agent;
18242
18574
  if (!agent) return item;
18243
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;
18244
18578
  return {
18245
18579
  ...item,
18246
18580
  ...state ? { availability: state.status } : {},
18247
- ...updatingVersions.has(JSON.stringify([selectedHost.hostId, agent])) ? { availability: "updating" } : {},
18581
+ ...version2 ? { version: version2 } : {},
18582
+ ...version2?.updating ? { availability: "updating" } : {},
18248
18583
  ...state?.error ? { installError: state.error } : {},
18249
18584
  ...diagnostics?.installation && harnessHasInstallCommands(item.agentSnapshot?.plugin) ? {
18250
18585
  install: () => {
@@ -18281,30 +18616,7 @@ ${error51.stderrTail}`] : []
18281
18616
  runRefresh
18282
18617
  );
18283
18618
  launchControls = { hostId: selectedHost.hostId, itemKey: item.key, element };
18284
- const agent = item.agentSnapshot?.agent;
18285
- const installation = diagnostics?.installation?.bind(diagnostics);
18286
- if (agent && item.agentSnapshot?.availability !== "notInstalled" && item.availability !== "installing" && item.availability !== "checking" && installation) {
18287
- const key = JSON.stringify([selectedHost.hostId, agent]);
18288
- let panel = versionPanels.get(key);
18289
- if (!panel) {
18290
- const hostId2 = selectedHost.hostId;
18291
- panel = createHarnessVersionPanel(document2, messages, context.signal, agent, {
18292
- async run(action) {
18293
- if (action !== "update") return installation(hostId2, agent, action);
18294
- updatingVersions.add(key);
18295
- if (diagnostics) render(diagnostics.snapshot());
18296
- try {
18297
- return await installation(hostId2, agent, action);
18298
- } finally {
18299
- updatingVersions.delete(key);
18300
- if (!context.signal.aborted && diagnostics) render(diagnostics.snapshot());
18301
- }
18302
- }
18303
- });
18304
- versionPanels.set(key, panel);
18305
- }
18306
- inspector.append(panel);
18307
- }
18619
+ if (item.version) inspector.append(item.version.element);
18308
18620
  };
18309
18621
  const pinnedItem = items.find((item) => item.key === "renderer-adapter");
18310
18622
  if (pinnedItem) {
@@ -18460,7 +18772,14 @@ ${error51.stderrTail}`] : []
18460
18772
  }
18461
18773
  const selectedItem = items.find((item) => item.key === selectedItemKey) ?? items[0];
18462
18774
  if (selectedItem) selectItem(selectedItem);
18463
- 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);
18464
18783
  layout.append(list, inspector);
18465
18784
  content.replaceChildren(layout);
18466
18785
  disposeHostScroller = configureHostScroller(
@@ -18759,16 +19078,23 @@ ${error51.stderrTail}`] : []
18759
19078
  launchPathSaveError: "Could not save. Enter an existing absolute installation folder on this Host and check configuration permissions.",
18760
19079
  connectionOfficialWebsite: "Visit official website",
18761
19080
  connectionOpenHarnessWeb: "Open DeepSeek Harness Web",
18762
- connectionDeepSeekTestedVersions: "Supported DSH versions: 0.1.7-rc.1, 0.1.7-rc.2, 0.2.0-rc.1 and 0.2.0-rc.2. Versions newer than 0.2.0-rc.2 can be tried, but compatibility may be limited; versions older than 0.1.7-rc.1 must be upgraded first.",
19081
+ connectionDeepSeekTestedVersions: "Supported DSH versions: 0.1.7-rc.1, 0.1.7-rc.2, 0.2.0-rc.1, 0.2.0-rc.2, 0.2.1-rc.1 and 0.2.1-rc.2. Versions newer than 0.2.1-rc.2 can be tried, but compatibility may be limited; versions older than 0.1.7-rc.1 must be upgraded first.",
18763
19082
  connectionInstall: "Install",
18764
19083
  connectionInstallDescription: "This Harness was not detected.",
18765
19084
  connectionInstallRunning: "Installing on the selected Host. You can leave this page; login or setup may still be required afterward.",
18766
19085
  harnessVersion: harnessVersionEnglish,
18767
19086
  harnessVersionTitle: "Harness CLI version",
19087
+ harnessVersionApplicationTitle: "Application version",
18768
19088
  harnessVersionCurrent: "Current version",
18769
19089
  harnessVersionLatest: "Latest version",
18770
19090
  harnessVersionChecking: "Checking versions\u2026",
19091
+ harnessVersionAvailable: "Update available",
18771
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}.",
18772
19098
  harnessVersionUpdating: "Updating\u2026",
18773
19099
  harnessVersionUpToDate: "Up to date",
18774
19100
  harnessVersionUpdated: "Update verified. New sessions will use the new version.",
@@ -19032,16 +19358,23 @@ ${error51.stderrTail}`] : []
19032
19358
  launchPathSaveError: "\u4FDD\u5B58\u5931\u8D25\u3002\u8BF7\u586B\u5199\u6B64 Host \u4E0A\u5B9E\u9645\u5B58\u5728\u7684\u5B89\u88C5\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5E76\u786E\u8BA4\u914D\u7F6E\u76EE\u5F55\u53EF\u5199\u3002",
19033
19359
  connectionOfficialWebsite: "\u8BBF\u95EE\u5B98\u7F51",
19034
19360
  connectionOpenHarnessWeb: "\u6253\u5F00 DeepSeek Harness Web",
19035
- connectionDeepSeekTestedVersions: "\u652F\u6301 DSH \u7248\u672C\uFF1A0.1.7-rc.1\u30010.1.7-rc.2\u30010.2.0-rc.1 \u548C 0.2.0-rc.2\u3002\u9AD8\u4E8E 0.2.0-rc.2 \u7684\u7248\u672C\u53EF\u4EE5\u5C1D\u8BD5\u8FDE\u63A5\uFF0C\u4F46\u9002\u914D\u5EA6\u53EF\u80FD\u6709\u9650\uFF1B\u4F4E\u4E8E 0.1.7-rc.1 \u7684\u7248\u672C\u9700\u8981\u5148\u5347\u7EA7\u3002",
19361
+ connectionDeepSeekTestedVersions: "\u652F\u6301 DSH \u7248\u672C\uFF1A0.1.7-rc.1\u30010.1.7-rc.2\u30010.2.0-rc.1\u30010.2.0-rc.2\u30010.2.1-rc.1 \u548C 0.2.1-rc.2\u3002\u9AD8\u4E8E 0.2.1-rc.2 \u7684\u7248\u672C\u53EF\u4EE5\u5C1D\u8BD5\u8FDE\u63A5\uFF0C\u4F46\u9002\u914D\u5EA6\u53EF\u80FD\u6709\u9650\uFF1B\u4F4E\u4E8E 0.1.7-rc.1 \u7684\u7248\u672C\u9700\u8981\u5148\u5347\u7EA7\u3002",
19036
19362
  connectionInstall: "\u5B89\u88C5",
19037
19363
  connectionInstallDescription: "\u5C1A\u672A\u68C0\u6D4B\u5230\u8BE5 Harness\u3002",
19038
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",
19039
19365
  harnessVersion: harnessVersionChinese,
19040
19366
  harnessVersionTitle: "Harness CLI \u7248\u672C",
19367
+ harnessVersionApplicationTitle: "\u5E94\u7528\u7248\u672C",
19041
19368
  harnessVersionCurrent: "\u5F53\u524D\u7248\u672C",
19042
19369
  harnessVersionLatest: "\u6700\u65B0\u7248\u672C",
19043
19370
  harnessVersionChecking: "\u6B63\u5728\u68C0\u67E5\u7248\u672C\u2026",
19371
+ harnessVersionAvailable: "\u6709\u66F4\u65B0",
19044
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",
19045
19378
  harnessVersionUpdating: "\u6B63\u5728\u66F4\u65B0\u2026",
19046
19379
  harnessVersionUpToDate: "\u5DF2\u662F\u6700\u65B0",
19047
19380
  harnessVersionUpdated: "\u5DF2\u786E\u8BA4\u66F4\u65B0\u6210\u529F\uFF0C\u65B0\u4F1A\u8BDD\u5C06\u4F7F\u7528\u65B0\u7248\u672C\u3002",
@@ -19828,7 +20161,6 @@ ${error51.stderrTail}`] : []
19828
20161
  // src/renderer-usage-control.ts
19829
20162
  var ENGLISH_USAGE_MESSAGES = Object.freeze({
19830
20163
  usage: "Usage",
19831
- account: "Account",
19832
20164
  context: "Context",
19833
20165
  recordedCredits: "Recorded usage",
19834
20166
  latestCacheHit: "Latest cache hit (CH)",
@@ -19854,7 +20186,6 @@ ${error51.stderrTail}`] : []
19854
20186
  });
19855
20187
  var CHINESE_USAGE_MESSAGES = Object.freeze({
19856
20188
  usage: "\u7528\u91CF",
19857
- account: "\u8D26\u53F7",
19858
20189
  context: "\u4E0A\u4E0B\u6587",
19859
20190
  recordedCredits: "\u5DF2\u8BB0\u5F55\u6D88\u8017",
19860
20191
  latestCacheHit: "\u6700\u8FD1\u7F13\u5B58\u547D\u4E2D\uFF08CH\uFF09",
@@ -19966,10 +20297,10 @@ ${error51.stderrTail}`] : []
19966
20297
  popover.style.color = "CanvasText";
19967
20298
  popover.style.boxShadow = "light-dark(0 10px 24px rgba(15, 23, 42, 0.12), 0 20px 45px rgba(0, 0, 0, 0.42)), 0 2px 8px light-dark(rgba(15, 23, 42, 0.06), rgba(0, 0, 0, 0.28))";
19968
20299
  }
19969
- function addDetailRow(parent, label, value, wrap = false) {
20300
+ function addDetailRow(parent, label, value) {
19970
20301
  const row = document.createElement("div");
19971
20302
  row.style.display = "grid";
19972
- row.style.gridTemplateColumns = wrap ? "auto minmax(0, 1fr)" : "minmax(0, 1fr) auto";
20303
+ row.style.gridTemplateColumns = "minmax(0, 1fr) auto";
19973
20304
  row.style.gap = "20px";
19974
20305
  row.style.padding = "4px 0";
19975
20306
  const labelElement = document.createElement("span");
@@ -19979,10 +20310,6 @@ ${error51.stderrTail}`] : []
19979
20310
  valueElement.textContent = value;
19980
20311
  valueElement.style.fontVariantNumeric = "tabular-nums";
19981
20312
  valueElement.style.textAlign = "right";
19982
- if (wrap) {
19983
- valueElement.style.overflowWrap = "anywhere";
19984
- valueElement.title = value;
19985
- }
19986
20313
  row.append(labelElement, valueElement);
19987
20314
  parent.append(row);
19988
20315
  }
@@ -20033,14 +20360,13 @@ ${error51.stderrTail}`] : []
20033
20360
  if (note) addNote(parent, note);
20034
20361
  }
20035
20362
  }
20036
- function renderDetails(popover, usage, messages, locale, accountName) {
20363
+ function renderDetails(popover, usage, messages, locale) {
20037
20364
  popover.replaceChildren();
20038
20365
  const heading = document.createElement("div");
20039
20366
  heading.textContent = messages.usage;
20040
20367
  heading.style.fontWeight = "600";
20041
20368
  heading.style.marginBottom = "6px";
20042
20369
  popover.append(heading);
20043
- if (accountName) addDetailRow(popover, messages.account, accountName, true);
20044
20370
  if (usage?.contextUsagePercent !== void 0) {
20045
20371
  addContextRow(
20046
20372
  popover,
@@ -20272,7 +20598,7 @@ ${error51.stderrTail}`] : []
20272
20598
  document.body.append(popover);
20273
20599
  return control;
20274
20600
  }
20275
- function renderRendererUsageControl(control, usage, locale = control.locale, accountName = null) {
20601
+ function renderRendererUsageControl(control, usage, locale = control.locale) {
20276
20602
  control.locale = locale;
20277
20603
  const messages = rendererUsageMessages(locale);
20278
20604
  control.popover.setAttribute("aria-label", messages.threadUsageDetails);
@@ -20281,7 +20607,7 @@ ${error51.stderrTail}`] : []
20281
20607
  const totalCostUsd = usage?.totalCostUsd;
20282
20608
  const hasContext = usage?.contextUsedTokens !== void 0 && usage.contextWindowTokens !== void 0;
20283
20609
  const hasTokenUsage = usage?.totalTokens !== void 0 || usage?.inputTokens !== void 0 || usage?.cachedInputTokens !== void 0 || usage?.cacheWriteInputTokens !== void 0 || usage?.outputTokens !== void 0;
20284
- const visible = rendererUsageHasDisplayData(usage) || Boolean(accountName);
20610
+ const visible = rendererUsageHasDisplayData(usage);
20285
20611
  control.root.style.display = visible ? "inline-flex" : "none";
20286
20612
  if (!visible) {
20287
20613
  closePopover(control);
@@ -20315,7 +20641,7 @@ ${error51.stderrTail}`] : []
20315
20641
  control.trigger.setAttribute("aria-label", accessibleSummary);
20316
20642
  control.trigger.title = accessibleSummary;
20317
20643
  control.label.textContent = compactSummary;
20318
- renderDetails(control.popover, usage, messages, locale, accountName);
20644
+ renderDetails(control.popover, usage, messages, locale);
20319
20645
  return true;
20320
20646
  }
20321
20647
 
@@ -20584,7 +20910,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
20584
20910
  loadingPermissions: "Loading permissions...",
20585
20911
  selecting: "Selecting...",
20586
20912
  permissionsUnavailable: "Permissions unavailable",
20587
- permissionModeFixedAtCreate: "Grok fixes its Permission Mode when the Session is created. Start a new Thread to change it.",
20913
+ permissionModeFixedAtCreate: "This Permission Mode is fixed when the Session is created. Start a new Thread to change it.",
20588
20914
  codexUsageGateUnavailable: "Could not separate this Harness from the Codex usage limit in this Desktop version. Codex usage limits still apply to sending.",
20589
20915
  modelSelectionRejected: "Couldn't switch Model",
20590
20916
  thinkingSelectionRejected: "Couldn't switch Thinking option"
@@ -20601,127 +20927,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
20601
20927
  loadingPermissions: "\u6B63\u5728\u52A0\u8F7D\u6743\u9650...",
20602
20928
  selecting: "\u6B63\u5728\u9009\u62E9...",
20603
20929
  permissionsUnavailable: "\u6743\u9650\u4E0D\u53EF\u7528",
20604
- permissionModeFixedAtCreate: "Grok \u7684\u6743\u9650\u6A21\u5F0F\u5728\u4F1A\u8BDD\u521B\u5EFA\u65F6\u786E\u5B9A\uFF0C\u5982\u9700\u66F4\u6539\u8BF7\u65B0\u5EFA\u4F1A\u8BDD",
20930
+ permissionModeFixedAtCreate: "\u6B64\u6743\u9650\u6A21\u5F0F\u5728\u4F1A\u8BDD\u521B\u5EFA\u65F6\u786E\u5B9A\uFF0C\u5982\u9700\u66F4\u6539\u8BF7\u65B0\u5EFA\u4F1A\u8BDD",
20605
20931
  codexUsageGateUnavailable: "\u5F53\u524D Desktop \u7248\u672C\u65E0\u6CD5\u5C06\u6B64 Harness \u4E0E Codex \u989D\u5EA6\u9650\u5236\u5206\u79BB\uFF0C\u53D1\u9001\u4ECD\u53D7 Codex \u989D\u5EA6\u9650\u5236",
20606
20932
  modelSelectionRejected: "\u65E0\u6CD5\u5207\u6362\u6A21\u578B",
20607
20933
  thinkingSelectionRejected: "\u65E0\u6CD5\u5207\u6362\u601D\u8003\u9009\u9879"
20608
20934
  });
20609
- var ENGLISH_PERMISSION_MODE_LABELS = /* @__PURE__ */ new Map([
20610
- ["read-only", "Read only"],
20611
- ["workspace-write", "Workspace write"],
20612
- ["danger-full-access", "Full access (dangerous)"]
20613
- ]);
20614
- var CHINESE_PERMISSION_MODE_LABELS = /* @__PURE__ */ new Map([
20615
- ["Always ask", "\u59CB\u7EC8\u8BE2\u95EE"],
20616
- ["Write", "\u5199\u5165"],
20617
- ["Full access", "\u5B8C\u5168\u8BBF\u95EE"],
20618
- ["Plan mode", "\u89C4\u5212\u6A21\u5F0F"],
20619
- ["Default", "\u9ED8\u8BA4"],
20620
- ["Accept edits", "\u63A5\u53D7\u7F16\u8F91"],
20621
- ["Auto mode", "\u81EA\u52A8\u6A21\u5F0F"],
20622
- ["Bypass permissions", "\u7ED5\u8FC7\u6743\u9650"],
20623
- ["Ask", "\u8BE2\u95EE"],
20624
- ["Auto", "\u81EA\u52A8"],
20625
- ["Always approve", "\u59CB\u7EC8\u6279\u51C6"],
20626
- ["Allow all", "\u5168\u90E8\u5141\u8BB8"],
20627
- ["Read only", "\u53EA\u8BFB"],
20628
- ["Workspace write", "\u5DE5\u4F5C\u533A\u5199\u5165"],
20629
- ["Full access (dangerous)", "\u5B8C\u5168\u8BBF\u95EE\uFF08\u5371\u9669\uFF09"],
20630
- ["Configured permissions", "\u4F7F\u7528\u5DF2\u914D\u7F6E\u6743\u9650"],
20631
- ["Skip permissions", "\u8DF3\u8FC7\u6743\u9650\u68C0\u67E5"],
20632
- ["Always Ask", "\u59CB\u7EC8\u8BE2\u95EE"],
20633
- ["Accept Edits", "\u63A5\u53D7\u7F16\u8F91"],
20634
- ["Plan", "\u89C4\u5212\u6A21\u5F0F"],
20635
- ["Don't Ask", "\u4E0D\u8BE2\u95EE"],
20636
- ["Bypass Permissions", "\u7ED5\u8FC7\u6743\u9650"],
20637
- ["Full Access", "\u5B8C\u5168\u8BBF\u95EE"],
20638
- ["Delegate", "\u7531\u7236\u4F1A\u8BDD\u7BA1\u7406"]
20639
- ]);
20640
- var CHINESE_PERMISSION_MODE_DESCRIPTIONS = /* @__PURE__ */ new Map([
20641
- [
20642
- "Automatically allow reads and ask before write or execution actions.",
20643
- "\u81EA\u52A8\u5141\u8BB8\u8BFB\u53D6\uFF1B\u5199\u5165\u6216\u6267\u884C\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"
20644
- ],
20645
- [
20646
- "Automatically allow reads and writes; ask before execution actions.",
20647
- "\u81EA\u52A8\u5141\u8BB8\u8BFB\u53D6\u548C\u5199\u5165\uFF1B\u6267\u884C\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"
20648
- ],
20649
- ["Run all tool actions without approval prompts.", "\u65E0\u9700\u6279\u51C6\u63D0\u793A\u5373\u53EF\u8FD0\u884C\u6240\u6709\u5DE5\u5177\u64CD\u4F5C\u3002"],
20650
- [
20651
- "Explore and prepare a plan; approval exits planning and resumes the previous permission mode.",
20652
- "\u63A2\u7D22\u5E76\u5236\u5B9A\u8BA1\u5212\uFF1B\u6279\u51C6\u8BA1\u5212\u540E\u9000\u51FA\u89C4\u5212\uFF0C\u6062\u590D\u6B64\u524D\u7684\u6743\u9650\u6A21\u5F0F\u3002"
20653
- ],
20654
- ["Ask before edits and other protected actions.", "\u7F16\u8F91\u548C\u5176\u4ED6\u53D7\u4FDD\u62A4\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"],
20655
- ["Allow file edits and ask for other protected actions.", "\u5141\u8BB8\u6587\u4EF6\u7F16\u8F91\uFF1B\u5176\u4ED6\u53D7\u4FDD\u62A4\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"],
20656
- ["Let Claude classify permission requests.", "\u7531 Claude \u5224\u65AD\u6743\u9650\u8BF7\u6C42\u3002"],
20657
- ["Skip Claude Code permission checks.", "\u8DF3\u8FC7 Claude Code \u6743\u9650\u68C0\u67E5\u3002"],
20658
- [
20659
- "Use Grok Build's default interactive approval policy.",
20660
- "\u4F7F\u7528 Grok Build \u7684\u9ED8\u8BA4\u4EA4\u4E92\u5F0F\u6279\u51C6\u7B56\u7565\u3002"
20661
- ],
20662
- ["Ask before protected tool actions.", "\u6267\u884C\u53D7\u4FDD\u62A4\u7684\u5DE5\u5177\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"],
20663
- [
20664
- "Let Grok Build decide which tool actions may run automatically.",
20665
- "\u7531 Grok Build \u51B3\u5B9A\u54EA\u4E9B\u5DE5\u5177\u64CD\u4F5C\u53EF\u81EA\u52A8\u8FD0\u884C\u3002"
20666
- ],
20667
- ["Approve all tool actions without prompting.", "\u65E0\u9700\u63D0\u793A\u5373\u53EF\u6279\u51C6\u6240\u6709\u5DE5\u5177\u64CD\u4F5C\u3002"],
20668
- ["Use OpenCode's configured permission rules.", "\u4F7F\u7528 OpenCode \u5DF2\u914D\u7F6E\u7684\u6743\u9650\u89C4\u5219\u3002"],
20669
- ["Ask before protected OpenCode actions.", "\u6267\u884C\u53D7\u4FDD\u62A4\u7684 OpenCode \u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"],
20670
- ["Allow OpenCode actions without approval prompts.", "\u5141\u8BB8 OpenCode \u64CD\u4F5C\uFF0C\u65E0\u9700\u6279\u51C6\u63D0\u793A\u3002"],
20671
- [
20672
- "Use Antigravity CLI permission rules; headless prompts are denied safely.",
20673
- "\u4F7F\u7528 Antigravity CLI \u6743\u9650\u89C4\u5219\uFF1B\u65E0\u754C\u9762\u8FD0\u884C\u65F6\u5B89\u5168\u62D2\u7EDD\u9700\u8981\u4EA4\u4E92\u786E\u8BA4\u7684\u8BF7\u6C42\u3002"
20674
- ],
20675
- ["Auto-approve every Antigravity CLI tool action.", "\u81EA\u52A8\u6279\u51C6\u6240\u6709 Antigravity CLI \u5DE5\u5177\u64CD\u4F5C\u3002"],
20676
- ["Prompts for permission on first use of each tool", "\u9996\u6B21\u4F7F\u7528\u6BCF\u79CD\u5DE5\u5177\u65F6\u8BF7\u6C42\u6743\u9650\u3002"],
20677
- [
20678
- "Automatically accepts file edit permissions for the session",
20679
- "\u81EA\u52A8\u63A5\u53D7\u672C\u4F1A\u8BDD\u4E2D\u7684\u6587\u4EF6\u7F16\u8F91\u6743\u9650\u3002"
20680
- ],
20681
- [
20682
- "Agent can analyze but not modify files or execute commands",
20683
- "Agent \u53EF\u4EE5\u5206\u6790\uFF0C\u4F46\u4E0D\u80FD\u4FEE\u6539\u6587\u4EF6\u6216\u6267\u884C\u547D\u4EE4\u3002"
20684
- ],
20685
- [
20686
- "An AI classifier reviews actions that would normally prompt: safe ones are auto-approved, risky ones are denied. If the classifier is unavailable, the action falls back to a prompt (or is denied when prompts cannot be shown)",
20687
- "\u7531 AI \u5206\u7C7B\u5668\u8BC4\u4F30\u539F\u672C\u9700\u8981\u8BE2\u95EE\u7684\u64CD\u4F5C\uFF1A\u5B89\u5168\u64CD\u4F5C\u81EA\u52A8\u6279\u51C6\uFF0C\u98CE\u9669\u64CD\u4F5C\u62D2\u7EDD\u3002\u5206\u7C7B\u5668\u4E0D\u53EF\u7528\u65F6\u6539\u4E3A\u8BE2\u95EE\uFF1B\u65E0\u6CD5\u663E\u793A\u8BE2\u95EE\u65F6\u62D2\u7EDD\u3002"
20688
- ],
20689
- [
20690
- "Does not show permission prompts; pre-approved and safe read-only actions run, everything else that would prompt is denied",
20691
- "\u4E0D\u663E\u793A\u6743\u9650\u8BE2\u95EE\uFF1B\u5DF2\u83B7\u6279\u51C6\u548C\u5B89\u5168\u7684\u53EA\u8BFB\u64CD\u4F5C\u53EF\u4EE5\u6267\u884C\uFF0C\u5176\u4F59\u539F\u672C\u9700\u8981\u8BE2\u95EE\u7684\u64CD\u4F5C\u4F1A\u88AB\u62D2\u7EDD\u3002"
20692
- ],
20693
- ["Skips all permission prompts", "\u8DF3\u8FC7\u5E38\u89C4\u6743\u9650\u8BE2\u95EE\u3002"],
20694
- [
20695
- "Skips ALL permission checks including dangerous commands for all agents",
20696
- "\u4E3A\u6240\u6709 Agent \u8DF3\u8FC7\u5168\u90E8\u6743\u9650\u68C0\u67E5\uFF0C\u5305\u62EC\u5371\u9669\u547D\u4EE4\u3002"
20697
- ],
20698
- ["Permissions managed by parent session", "\u7531\u7236\u4F1A\u8BDD\u7BA1\u7406\u6743\u9650\u3002"],
20699
- [
20700
- "Native agent mode with Cursor tool approvals",
20701
- "\u6267\u884C\u4EFB\u52A1\uFF0C\u53EF\u4FEE\u6539\u6587\u4EF6\u548C\u8FD0\u884C\u547D\u4EE4\uFF1B\u9700\u8981\u5BA1\u6279\u7684\u64CD\u4F5C\u4ECD\u4F1A\u8BF7\u6C42\u786E\u8BA4\u3002"
20702
- ],
20703
- ["Native read-only planning mode", "\u53EA\u8BFB\u5206\u6790\u4EE3\u7801\u5E76\u5236\u5B9A\u5B9E\u65BD\u8BA1\u5212\uFF0C\u4E0D\u4FEE\u6539\u9879\u76EE\u6587\u4EF6\u3002"],
20704
- ["Native read-only question mode", "\u53EA\u8BFB\u67E5\u770B\u4EE3\u7801\u5E76\u56DE\u7B54\u95EE\u9898\uFF0C\u4E0D\u4FEE\u6539\u9879\u76EE\u6587\u4EF6\u3002"]
20705
- ]);
20706
20935
  function rendererHarnessMessages(locale) {
20707
20936
  return locale === "zh-CN" ? CHINESE_HARNESS_MESSAGES : ENGLISH_HARNESS_MESSAGES;
20708
20937
  }
20709
20938
  function rendererHarnessCommandPresentation(command, locale) {
20710
- if (locale === "zh-CN" && command.invocation === "/compact") {
20711
- return { label: "\u538B\u7F29\u4E0A\u4E0B\u6587", description: "\u538B\u7F29\u5F53\u524D\u5BF9\u8BDD\u4E0A\u4E0B\u6587" };
20712
- }
20713
- return {
20714
- label: command.label,
20715
- description: command.description ?? command.label
20716
- };
20939
+ const presentation = localizeHarnessPresentation(command, locale);
20940
+ return { ...presentation, description: presentation.description ?? presentation.label };
20717
20941
  }
20718
20942
  function rendererPermissionModePresentation(mode, locale) {
20719
- const label = ENGLISH_PERMISSION_MODE_LABELS.get(mode.label) ?? mode.label;
20720
- if (locale !== "zh-CN") return { label, description: mode.description };
20721
- return {
20722
- label: CHINESE_PERMISSION_MODE_LABELS.get(label) ?? label,
20723
- description: mode.description === void 0 ? void 0 : CHINESE_PERMISSION_MODE_DESCRIPTIONS.get(mode.description) ?? mode.description
20724
- };
20943
+ return localizeHarnessPresentation(mode, locale);
20725
20944
  }
20726
20945
  function rendererLiveCommandsPendingNotice(locale, harnessLabel) {
20727
20946
  const harness2 = harnessLabel.replace(/\s*[((][^))]*[))]\s*$/u, "").trim() || harnessLabel;
@@ -22003,30 +22222,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22003
22222
  return messages.account;
22004
22223
  }
22005
22224
  function productLabel(product, locale = "en") {
22006
- if (product === "GrokBuild") return "Build";
22007
- if (product === "GrokChat") return "Chat";
22008
- if (product === "GrokImagine") return "Imagine";
22009
- if (product === "GrokVoice") return "Voice";
22010
- const messages = rendererCreditsMessages(locale);
22011
- if (product === "5-hour window") return messages.fiveHour;
22012
- if (product === "7-day window") return messages.sevenDay;
22013
- if (product === "Weekly window") return messages.weekly;
22014
- if (product.endsWith(" \xB7 5-hour window")) {
22015
- return `${product.slice(0, -"5-hour window".length)}${messages.fiveHour}`;
22016
- }
22017
- if (product.endsWith(" \xB7 7-day window")) {
22018
- return `${product.slice(0, -"7-day window".length)}${messages.sevenDay}`;
22019
- }
22020
- if (product.endsWith(" \xB7 Weekly window")) {
22021
- return `${product.slice(0, -"Weekly window".length)}${messages.weekly}`;
22022
- }
22023
- if (product.endsWith(" \xB7 5-hour")) {
22024
- return `${product.slice(0, -"5-hour".length)}${messages.fiveHour}`;
22025
- }
22026
- if (product.endsWith(" \xB7 7-day")) {
22027
- return `${product.slice(0, -"7-day".length)}${messages.sevenDay}`;
22028
- }
22029
- return product;
22225
+ return accountCreditsWindowLabel(product, locale, (period) => creditsPeriodLabel(period, locale));
22030
22226
  }
22031
22227
  function toneColor(tone) {
22032
22228
  if (tone === "hot") return "#c45c4a";
@@ -22067,7 +22263,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22067
22263
  top.style.marginBottom = "5px";
22068
22264
  const left = document.createElement("div");
22069
22265
  const label = document.createElement("div");
22070
- label.textContent = credits.label ? productLabel(credits.label, locale) : creditsPeriodLabel(credits.periodType, locale);
22266
+ label.textContent = productLabel(credits, locale);
22071
22267
  label.style.fontSize = "12.5px";
22072
22268
  label.style.fontWeight = "600";
22073
22269
  left.append(label);
@@ -22142,7 +22338,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22142
22338
  popover.append(renderCreditsHeader(credits, locale, messages));
22143
22339
  const tiles = (credits.productUsage ?? []).map(
22144
22340
  (product) => renderCreditsTile(
22145
- productLabel(product.product, locale),
22341
+ productLabel(product, locale),
22146
22342
  product.usagePercent,
22147
22343
  locale,
22148
22344
  messages,
@@ -22310,7 +22506,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22310
22506
  }
22311
22507
  const remaining = remainingPercent(accountCredits.usedPercent);
22312
22508
  const percent2 = formatRendererCreditsPercent(remaining);
22313
- const periodLabel = accountCredits.label ? productLabel(accountCredits.label, locale) : creditsPeriodLabel(accountCredits.periodType, locale);
22509
+ const periodLabel = accountCredits.label ? productLabel(accountCredits, locale) : creditsPeriodLabel(accountCredits.periodType, locale);
22314
22510
  const title = `${periodLabel} ${percent2}`;
22315
22511
  const tone = rendererCreditsTone(accountCredits.usedPercent);
22316
22512
  const ringSlot = control.trigger.querySelector("[data-codexhost-credits-ring]");
@@ -22324,7 +22520,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22324
22520
  })
22325
22521
  );
22326
22522
  }
22327
- if (label) label.textContent = percent2;
22523
+ if (label) label.textContent = `${Math.round(remaining)}%`;
22328
22524
  control.root.style.display = "inline-flex";
22329
22525
  control.trigger.setAttribute("aria-label", title);
22330
22526
  control.trigger.title = title;
@@ -22332,6 +22528,148 @@ Codex Fast mode prioritizes requests and may increase usage.`
22332
22528
  return true;
22333
22529
  }
22334
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
+
22335
22673
  // src/renderer-harness-command-control.ts
22336
22674
  var CONTROL_ATTRIBUTE2 = "data-codexhost-harness-command-control";
22337
22675
  var SVG_NS2 = "http://www.w3.org/2000/svg";
@@ -22464,7 +22802,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22464
22802
  return controlDescription(button2);
22465
22803
  }
22466
22804
  function isOwnedRendererControl(element) {
22467
- 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");
22468
22806
  }
22469
22807
  function isComposerSubmitButton(button2) {
22470
22808
  if (button2.type === "submit") return true;
@@ -22762,6 +23100,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
22762
23100
  refreshTrailingClusterPlacement(control);
22763
23101
  refreshUsagePlacement(control);
22764
23102
  refreshCreditsPlacement(control);
23103
+ control.composerIndicators?.place(
23104
+ control.nativePermissionModeControlVerified ? control.nativePermissionModeControl?.element ?? null : null
23105
+ );
22765
23106
  setNativeControlHidden(control.nativeModelControl, hideModel);
22766
23107
  setNativeControlHidden(control.nativeContextUsageControl, false);
22767
23108
  setNativeControlHidden(control.nativePermissionModeControl, hidePermissionMode);
@@ -22788,6 +23129,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22788
23129
  onSelectPermissionMode
22789
23130
  );
22790
23131
  const credits = mountRendererCreditsControl(composerId);
23132
+ const composerIndicators = mountRendererComposerIndicators(composer);
22791
23133
  const toolbar = sendButton.parentElement;
22792
23134
  const harnessCommands = mountRendererHarnessCommandControl(
22793
23135
  toolbar ?? composer,
@@ -22834,6 +23176,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22834
23176
  nativeContextUsageControl,
22835
23177
  nativePermissionModeControlVerified,
22836
23178
  credits,
23179
+ composerIndicators,
22837
23180
  usage: null,
22838
23181
  harnessCommands,
22839
23182
  sendButton,
@@ -22899,14 +23242,12 @@ Codex Fast mode prioritizes requests and may increase usage.`
22899
23242
  permissionModeVisible,
22900
23243
  locale
22901
23244
  );
22902
- const selectedCodexAccount = state.agent === "codex" && !ownershipError ? currentCodexAccount : void 0;
23245
+ control.composerIndicators?.render(
23246
+ permissionModeVisible ? usage?.composerIndicators ?? [] : [],
23247
+ locale
23248
+ );
22903
23249
  if (control.usage) {
22904
- renderRendererUsageControl(
22905
- control.usage,
22906
- usage,
22907
- locale,
22908
- selectedCodexAccount?.email ?? selectedCodexAccount?.label ?? null
22909
- );
23250
+ renderRendererUsageControl(control.usage, usage, locale);
22910
23251
  }
22911
23252
  control.harnessCommands.setLocale(locale);
22912
23253
  control.harnessCommands.root.hidden = state.agent === "codex";
@@ -22924,6 +23265,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22924
23265
  restoreNativeControl(control.nativeModelControl);
22925
23266
  restoreNativeControl(control.nativeContextUsageControl);
22926
23267
  restoreNativeControl(control.nativePermissionModeControl);
23268
+ control.composerIndicators?.dispose();
22927
23269
  control.credits.dispose();
22928
23270
  control.usage?.dispose();
22929
23271
  control.usage = null;
@@ -23046,62 +23388,141 @@ Codex Fast mode prioritizes requests and may increase usage.`
23046
23388
  }
23047
23389
  };
23048
23390
 
23391
+ // src/renderer-codex-usage-banner.ts
23392
+ var MAX_FIBERS = 2e4;
23393
+ function isRecord2(value) {
23394
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23395
+ }
23396
+ function isUsageBanner(props) {
23397
+ return isRecord2(props) && "banner" in props && "rateLimitStatus" in props && typeof props.sendBlocked === "boolean";
23398
+ }
23399
+ function isElement(value) {
23400
+ return isRecord2(value) && typeof value.setAttribute === "function" && isRecord2(value.style);
23401
+ }
23402
+ function hostElements(fiber, budget) {
23403
+ const result = [];
23404
+ const visit = (node) => {
23405
+ for (let current = node; isRecord2(current); current = current.sibling) {
23406
+ if (budget.remaining-- <= 0) return;
23407
+ const { stateNode } = current;
23408
+ if (isElement(stateNode)) result.push(stateNode);
23409
+ else visit(current.child);
23410
+ }
23411
+ };
23412
+ visit(fiber.child);
23413
+ return result;
23414
+ }
23415
+ function findCodexUsageBannerElements(owner) {
23416
+ if (!isRecord2(owner)) return [];
23417
+ const budget = { remaining: MAX_FIBERS };
23418
+ const found = [];
23419
+ const stack = [owner.child];
23420
+ while (stack.length > 0 && budget.remaining > 0) {
23421
+ const node = stack.pop();
23422
+ if (!isRecord2(node)) continue;
23423
+ budget.remaining -= 1;
23424
+ const fiber = node;
23425
+ stack.push(fiber.sibling);
23426
+ if (isUsageBanner(fiber.memoizedProps)) found.push(...hostElements(fiber, budget));
23427
+ else stack.push(fiber.child);
23428
+ }
23429
+ return budget.remaining > 0 ? found : [];
23430
+ }
23431
+ var HIDDEN_ATTRIBUTE = "data-codexhost-usage-banner-hidden";
23432
+ function createRendererCodexUsageBanner() {
23433
+ const hidden = /* @__PURE__ */ new Map();
23434
+ const show = (element) => {
23435
+ const previous = hidden.get(element);
23436
+ hidden.delete(element);
23437
+ if (!previous || element.getAttribute(HIDDEN_ATTRIBUTE) === null) return;
23438
+ element.removeAttribute(HIDDEN_ATTRIBUTE);
23439
+ if (previous.value) element.style.setProperty("display", previous.value, previous.priority);
23440
+ else element.style.removeProperty("display");
23441
+ };
23442
+ return {
23443
+ hide(owner) {
23444
+ const current = new Set(findCodexUsageBannerElements(owner));
23445
+ for (const element of [...hidden.keys()]) if (!current.has(element)) show(element);
23446
+ for (const element of current) {
23447
+ if (hidden.has(element)) continue;
23448
+ hidden.set(element, {
23449
+ value: element.style.getPropertyValue("display"),
23450
+ priority: element.style.getPropertyPriority("display")
23451
+ });
23452
+ element.setAttribute(HIDDEN_ATTRIBUTE, "");
23453
+ element.style.setProperty("display", "none", "important");
23454
+ }
23455
+ },
23456
+ restore() {
23457
+ for (const element of [...hidden.keys()]) show(element);
23458
+ }
23459
+ };
23460
+ }
23461
+
23049
23462
  // src/renderer-codex-usage-gate.ts
23050
23463
  var EDITOR_SELECTOR2 = '[contenteditable="true"][role="textbox"]';
23051
23464
  var MAX_HOOKS = 4096;
23052
23465
  var MAX_DOM_DEPTH = 12;
23053
23466
  var RETRY_DELAY_MS = 1e3;
23054
- function isRecord2(value) {
23467
+ function isRecord3(value) {
23055
23468
  return typeof value === "object" && value !== null && !Array.isArray(value);
23056
23469
  }
23057
23470
  function hooksOf(fiber) {
23058
23471
  const result = [];
23059
- let hook = isRecord2(fiber) ? fiber.memoizedState : null;
23060
- while (isRecord2(hook) && result.length < MAX_HOOKS) {
23472
+ let hook = isRecord3(fiber) ? fiber.memoizedState : null;
23473
+ while (isRecord3(hook) && result.length < MAX_HOOKS) {
23061
23474
  result.push(hook);
23062
23475
  hook = hook.next;
23063
23476
  }
23064
23477
  return hook == null ? result : [];
23065
23478
  }
23066
- function submitOwners(composer) {
23479
+ function submitOwners(composer, projections) {
23067
23480
  let element = composer.querySelector(EDITOR_SELECTOR2) ?? composer;
23068
23481
  for (let depth = 0; element && depth < MAX_DOM_DEPTH; depth += 1) {
23069
23482
  const key = Object.getOwnPropertyNames(element).find(
23070
23483
  (name) => name.startsWith("__reactFiber$")
23071
23484
  );
23072
23485
  if (key) {
23073
- const owners = committedReactAncestors(
23486
+ const ancestors = committedReactAncestors(
23074
23487
  Object.getOwnPropertyDescriptor(element, key)?.value
23075
- ).filter(
23076
- ({ memoizedProps: props }) => isRecord2(props) && "onLocalSubmitStart" in props && typeof props.submitDisabled === "boolean"
23077
23488
  );
23078
- return owners.filter((owner) => {
23079
- const gates = gateSubscriptions(owner);
23489
+ const scopes = [];
23490
+ ancestors.forEach(({ memoizedProps: props }, index) => {
23491
+ if (isRecord3(props) && "onLocalSubmitStart" in props && typeof props.submitDisabled === "boolean") {
23492
+ scopes.push({ owner: ancestors[index], fibers: ancestors.slice(0, index + 1) });
23493
+ }
23494
+ });
23495
+ const valid = scopes.filter((scope) => {
23496
+ const gates = gateSubscriptions(scope, projections);
23080
23497
  return !gates.invalid && gates.reserve.length > 0;
23081
23498
  });
23499
+ return valid.filter(
23500
+ (scope) => !valid.some((other) => other !== scope && scope.fibers.includes(other.owner))
23501
+ );
23082
23502
  }
23083
23503
  element = element.parentElement;
23084
23504
  }
23085
23505
  return [];
23086
23506
  }
23087
- function findSubmitOwner(composer) {
23088
- const owners = submitOwners(composer);
23089
- return owners.length === 1 ? owners[0] ?? null : null;
23507
+ function findSubmitScope(composer, projections) {
23508
+ const scopes = submitOwners(composer, projections);
23509
+ return scopes.length === 1 ? scopes[0] ?? null : null;
23090
23510
  }
23091
- function subscriptionAt(hook) {
23511
+ function subscriptionAt(hook, fiber) {
23092
23512
  const memo = hook.memoizedState;
23093
23513
  if (!Array.isArray(memo) || !Array.isArray(memo[1])) return null;
23094
23514
  const [subscriber, dependencies] = memo;
23095
23515
  if (dependencies.length !== 2) return null;
23096
23516
  const signal = dependencies[0];
23097
- const adapted = dependencies[1] === void 0 && isRecord2(signal) && typeof signal.get === "function" && typeof signal.subscribe === "function";
23517
+ const adapted = dependencies[1] === void 0 && isRecord3(signal) && typeof signal.get === "function" && typeof signal.subscribe === "function";
23098
23518
  const [store, atom] = adapted ? [signal.store, signal.atom] : dependencies;
23099
23519
  const instance = hook.next?.queue;
23100
23520
  const effect = hook.next?.next?.memoizedState;
23101
- if (!isRecord2(subscriber) || typeof subscriber.getSnapshot !== "function" || typeof subscriber.subscribe !== "function" || !isRecord2(store) || typeof store.get !== "function" || typeof store.sub !== "function" || !isRecord2(atom) || typeof atom.read !== "function" || "write" in atom || !isRecord2(instance) || typeof instance.value !== "boolean" || typeof instance.getSnapshot !== "function" || !isRecord2(effect) || typeof effect.create !== "function" || !Array.isArray(effect.deps) || effect.deps[0] !== subscriber.subscribe) {
23521
+ if (!isRecord3(subscriber) || typeof subscriber.getSnapshot !== "function" || typeof subscriber.subscribe !== "function" || !isRecord3(store) || typeof store.get !== "function" || typeof store.sub !== "function" || !isRecord3(atom) || typeof atom.read !== "function" || "write" in atom || !isRecord3(instance) || typeof instance.value !== "boolean" || typeof instance.getSnapshot !== "function" || !isRecord3(effect) || typeof effect.create !== "function" || !Array.isArray(effect.deps) || effect.deps[0] !== subscriber.subscribe) {
23102
23522
  return null;
23103
23523
  }
23104
23524
  return {
23525
+ fiber,
23105
23526
  subscriber,
23106
23527
  store,
23107
23528
  atom,
@@ -23139,10 +23560,10 @@ Codex Fast mode prioritizes requests and may increase usage.`
23139
23560
  visiting.delete(dependency);
23140
23561
  }
23141
23562
  }
23142
- if (!isRecord2(value)) return value;
23563
+ if (!isRecord3(value)) return value;
23143
23564
  if (typeof value.hardBlocked === "boolean") return trace(value, "reserve");
23144
23565
  if ("authMethod" in value) return trace(value, "auth");
23145
- if (isRecord2(value.data) && isRecord2(value.data.rate_limit)) {
23566
+ if (isRecord3(value.data) && isRecord3(value.data.rate_limit)) {
23146
23567
  return {
23147
23568
  ...value,
23148
23569
  data: { ...value.data, rate_limit: trace(value.data.rate_limit, "limit") }
@@ -23158,12 +23579,13 @@ Codex Fast mode prioritizes requests and may increase usage.`
23158
23579
  }
23159
23580
  const reserve = read.has("reserve.hardBlocked") && !read.has("reserve.active");
23160
23581
  const account = read.has("auth.authMethod") && read.has("limit.allowed");
23161
- if (account && (read.has("reserve.hardBlocked") || read.has("reserve.active"))) return "mixed";
23582
+ if (account && read.has("reserve.hardBlocked")) return "mixed";
23162
23583
  if (reserve) return "reserve";
23163
23584
  if (account) return "account";
23164
23585
  return null;
23165
23586
  }
23166
- function snapshotsMatch({ subscriber, instance, store, atom }, projected) {
23587
+ function snapshotsMatch({ subscriber, instance, store, atom }, projections) {
23588
+ const projected = projections?.get(instance);
23167
23589
  try {
23168
23590
  if (typeof subscriber.createRender === "function" && subscriber.createRender() != null) {
23169
23591
  return false;
@@ -23176,35 +23598,40 @@ Codex Fast mode prioritizes requests and may increase usage.`
23176
23598
  return false;
23177
23599
  }
23178
23600
  }
23179
- function gateSubscriptions(owner, projections) {
23601
+ function gateSubscriptions(scope, projections) {
23180
23602
  const found = {
23181
23603
  account: [],
23182
23604
  reserve: [],
23183
23605
  invalid: false
23184
23606
  };
23185
- for (const hook of hooksOf(owner)) {
23186
- const subscription = subscriptionAt(hook);
23187
- const kind = subscription && gateKind(subscription);
23188
- if (subscription && kind) {
23189
- if (kind !== "mixed" && snapshotsMatch(subscription, projections?.get(kind))) {
23190
- found[kind].push(subscription);
23191
- } else found.invalid = true;
23607
+ for (const fiber of scope.fibers) {
23608
+ for (const hook of hooksOf(fiber)) {
23609
+ const subscription = subscriptionAt(hook, fiber);
23610
+ const kind = subscription && gateKind(subscription);
23611
+ if (subscription && kind) {
23612
+ if (kind !== "mixed" && snapshotsMatch(subscription, projections)) {
23613
+ found[kind].push(subscription);
23614
+ } else found.invalid = true;
23615
+ }
23192
23616
  }
23193
23617
  }
23194
23618
  return found;
23195
23619
  }
23196
- function discoverGates(owner, projections) {
23197
- const gates = gateSubscriptions(owner, projections);
23620
+ function discoverGates(scope, projections) {
23621
+ const gates = gateSubscriptions(scope, projections);
23198
23622
  if (gates.invalid) return null;
23199
- const found = /* @__PURE__ */ new Map();
23623
+ const found = [];
23200
23624
  for (const kind of ["account", "reserve"]) {
23201
- const subscriptions = gates[kind];
23202
- if (subscriptions.length > 1) return null;
23203
- if (subscriptions[0]) found.set(kind, subscriptions[0]);
23625
+ const fibers = /* @__PURE__ */ new Set();
23626
+ for (const subscription of gates[kind]) {
23627
+ if (fibers.has(subscription.fiber)) return null;
23628
+ fibers.add(subscription.fiber);
23629
+ found.push([kind, subscription]);
23630
+ }
23204
23631
  }
23205
23632
  return found;
23206
23633
  }
23207
- function project2({ subscriber, store, atom, instance, subscribeEffect }) {
23634
+ function project2(kind, { fiber, subscriber, store, atom, instance, subscribeEffect }) {
23208
23635
  for (const [target, key] of [
23209
23636
  [store, "sub"],
23210
23637
  [subscriber, "getSnapshot"],
@@ -23235,6 +23662,8 @@ Codex Fast mode prioritizes requests and may increase usage.`
23235
23662
  instance.getSnapshot = allowed;
23236
23663
  if (instance.value !== false) onChange();
23237
23664
  return {
23665
+ kind,
23666
+ fiber,
23238
23667
  subscriber,
23239
23668
  instance,
23240
23669
  restore() {
@@ -23244,13 +23673,11 @@ Codex Fast mode prioritizes requests and may increase usage.`
23244
23673
  }
23245
23674
  };
23246
23675
  }
23247
- function isBound(owner, projections) {
23248
- const alternate = isRecord2(owner.alternate) ? owner.alternate : null;
23249
- const bound = [...projections];
23250
- return [owner, alternate].some((fiber) => {
23251
- const hooks = hooksOf(fiber);
23252
- return bound.every(
23253
- ({ subscriber, instance }) => hooks.some(
23676
+ function isBound(projections) {
23677
+ return [...projections].every(({ fiber, subscriber, instance }) => {
23678
+ const alternate = isRecord3(fiber.alternate) ? fiber.alternate : null;
23679
+ return [fiber, alternate].some(
23680
+ (candidate) => hooksOf(candidate).some(
23254
23681
  (hook) => Array.isArray(hook.memoizedState) && hook.memoizedState[0] === subscriber && hook.next?.queue === instance
23255
23682
  )
23256
23683
  );
@@ -23261,7 +23688,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
23261
23688
  const projections = /* @__PURE__ */ new Map();
23262
23689
  let retryAt = 0;
23263
23690
  let allowed = false;
23691
+ const banner = createRendererCodexUsageBanner();
23264
23692
  const release = () => {
23693
+ banner.restore();
23265
23694
  const previous = [...projections.values()];
23266
23695
  owner = null;
23267
23696
  projections.clear();
@@ -23273,26 +23702,33 @@ Codex Fast mode prioritizes requests and may increase usage.`
23273
23702
  }
23274
23703
  };
23275
23704
  const bind = () => {
23276
- const current = owner ?? findSubmitOwner(composer);
23705
+ const current = findSubmitScope(composer, projections);
23277
23706
  const gates = current && discoverGates(current, projections);
23278
- if (!current || !gates?.has("reserve")) throw new Error("Codex usage gate is unavailable");
23707
+ if (!current || !gates?.some(([kind]) => kind === "reserve")) {
23708
+ throw new Error("Codex usage gate is unavailable");
23709
+ }
23279
23710
  owner = current;
23280
23711
  for (const [kind, gate] of gates) {
23281
- if (!projections.has(kind)) projections.set(kind, project2(gate));
23712
+ if (!projections.has(gate.instance)) projections.set(gate.instance, project2(kind, gate));
23282
23713
  }
23283
23714
  return "bypassed";
23284
23715
  };
23285
23716
  const apply = () => {
23717
+ const status = applyGate();
23718
+ if (status === "bypassed" && owner) banner.hide(owner.owner);
23719
+ else banner.restore();
23720
+ return status;
23721
+ };
23722
+ const applyGate = () => {
23286
23723
  if (!allowed || !composer.isConnected) {
23287
23724
  release();
23288
23725
  retryAt = 0;
23289
23726
  return "native";
23290
23727
  }
23291
- if (owner && !isBound(owner, projections.values())) {
23728
+ if (owner && !isBound(projections.values())) {
23292
23729
  release();
23293
23730
  retryAt = 0;
23294
23731
  }
23295
- if (owner && projections.size === 2) return "bypassed";
23296
23732
  if (Date.now() < retryAt) return owner ? "bypassed" : "unsupported";
23297
23733
  retryAt = Date.now() + RETRY_DELAY_MS;
23298
23734
  try {
@@ -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) {
@@ -23417,13 +23873,13 @@ Codex Fast mode prioritizes requests and may increase usage.`
23417
23873
  }
23418
23874
 
23419
23875
  // src/renderer-native-thread.ts
23420
- function isRecord3(value) {
23876
+ function isRecord4(value) {
23421
23877
  return typeof value === "object" && value !== null && !Array.isArray(value);
23422
23878
  }
23423
23879
  async function verifyNativeCodexThread(sendRequest, threadId) {
23424
23880
  const native = await sendRequest("thread/read", { threadId, includeTurns: false });
23425
- const thread = isRecord3(native) ? native.thread : null;
23426
- if (!isRecord3(thread) || thread.id !== threadId || typeof thread.modelProvider !== "string" || !thread.modelProvider || thread.modelProvider === "codexhost" || typeof thread.cliVersion !== "string" || !thread.cliVersion || thread.cliVersion === "codexhost") {
23881
+ const thread = isRecord4(native) ? native.thread : null;
23882
+ if (!isRecord4(thread) || thread.id !== threadId || typeof thread.modelProvider !== "string" || !thread.modelProvider || thread.modelProvider === "codexhost" || typeof thread.cliVersion !== "string" || !thread.cliVersion || thread.cliVersion === "codexhost") {
23427
23883
  throw new Error("Native Thread response cannot establish Codex ownership");
23428
23884
  }
23429
23885
  }
@@ -23502,15 +23958,15 @@ Codex Fast mode prioritizes requests and may increase usage.`
23502
23958
  var CODEX_ACCOUNT_LIST_METHOD = "codexhost/account/list";
23503
23959
  var CODEX_ACCOUNT_REFRESH_METHOD = "codexhost/account/refresh";
23504
23960
  var CODEX_ACCOUNT_CHANGED_METHOD = "codexhost/account/changed";
23505
- function isRecord4(value) {
23961
+ function isRecord5(value) {
23506
23962
  return typeof value === "object" && value !== null && !Array.isArray(value);
23507
23963
  }
23508
23964
  function notifiedThreadId(notification) {
23509
- if (!isRecord4(notification) || !THREAD_USAGE_REFRESH_METHODS.includes(notification.method)) {
23965
+ if (!isRecord5(notification) || !THREAD_USAGE_REFRESH_METHODS.includes(notification.method)) {
23510
23966
  return null;
23511
23967
  }
23512
23968
  const params = notification.params;
23513
- if (!isRecord4(params)) return null;
23969
+ if (!isRecord5(params)) return null;
23514
23970
  const parsed = hostThreadIdSchema.safeParse(params.threadId);
23515
23971
  return parsed.success ? parsed.data : null;
23516
23972
  }
@@ -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(
@@ -23828,25 +24286,28 @@ Codex Fast mode prioritizes requests and may increase usage.`
23828
24286
  throw new Error("Renderer Account notification callback is unavailable");
23829
24287
  }
23830
24288
  return notifications.addNotificationCallback(CODEX_ACCOUNT_CHANGED_METHOD, (notification) => {
23831
- if (!isRecord4(notification) || notification.method !== CODEX_ACCOUNT_CHANGED_METHOD) return;
24289
+ if (!isRecord5(notification) || notification.method !== CODEX_ACCOUNT_CHANGED_METHOD) return;
23832
24290
  const state = codexAccountChangedSchema.safeParse(notification.params);
23833
24291
  if (state.success) listener(state.data);
23834
24292
  });
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
- function isRecord5(value) {
24301
+ function isRecord6(value) {
23841
24302
  return value !== null && typeof value === "object" && !Array.isArray(value);
23842
24303
  }
23843
24304
  function installRendererExternalQueue(target) {
23844
- if (!isRecord5(target) || typeof target.getTurnCoordinator !== "function" || typeof target.getConversation !== "function") {
24305
+ if (!isRecord6(target) || typeof target.getTurnCoordinator !== "function" || typeof target.getConversation !== "function") {
23845
24306
  return null;
23846
24307
  }
23847
24308
  const coordinator = target.getTurnCoordinator();
23848
- const queue = isRecord5(coordinator) ? coordinator.serverQueue : null;
23849
- if (!isRecord5(queue) || typeof queue.isEnabled !== "function") return null;
24309
+ const queue = isRecord6(coordinator) ? coordinator.serverQueue : null;
24310
+ if (!isRecord6(queue) || typeof queue.isEnabled !== "function") return null;
23850
24311
  const descriptor = Object.getOwnPropertyDescriptor(queue, "isEnabled");
23851
24312
  if (!descriptor?.writable) return null;
23852
24313
  const original = queue.isEnabled;
@@ -23855,7 +24316,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23855
24316
  const threadId = args[0];
23856
24317
  if (typeof threadId === "string") {
23857
24318
  const conversation = getConversation.call(target, threadId);
23858
- if (isRecord5(conversation) && conversation.id === threadId && // Reserved transport marker from externalThreadValue, retained by
24319
+ if (isRecord6(conversation) && conversation.id === threadId && // Reserved transport marker from externalThreadValue, retained by
23859
24320
  // Desktop in both newly created and hydrated conversation metadata.
23860
24321
  conversation.modelProvider === "codexhost") {
23861
24322
  return false;
@@ -23870,20 +24331,21 @@ 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
- function isRecord6(value) {
24336
+ function isRecord7(value) {
23875
24337
  return value !== null && typeof value === "object" && !Array.isArray(value);
23876
24338
  }
23877
24339
  function isManager(value) {
23878
- return isRecord6(value) && ["sendRequest", "steerTurn", "startTurn", "getTurnCoordinator"].every(
24340
+ return isRecord7(value) && ["sendRequest", "steerTurn", "startTurn", "getTurnCoordinator"].every(
23879
24341
  (key) => typeof value[key] === "function"
23880
24342
  );
23881
24343
  }
23882
24344
  function submissionHost(manager) {
23883
24345
  const coordinator = manager.getTurnCoordinator();
23884
- const options = isRecord6(coordinator) ? coordinator.options : null;
23885
- const host = isRecord6(options) ? options.submissionHost : null;
23886
- if (!isRecord6(host) || typeof host.getActiveTurnId !== "function" || typeof host.hasPendingTurnStart !== "function") {
24346
+ const options = isRecord7(coordinator) ? coordinator.options : null;
24347
+ const host = isRecord7(options) ? options.submissionHost : null;
24348
+ if (!isRecord7(host) || typeof host.getActiveTurnId !== "function" || typeof host.hasPendingTurnStart !== "function") {
23887
24349
  throw new Error("Desktop turn submission binding is unavailable");
23888
24350
  }
23889
24351
  return {
@@ -23925,20 +24387,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
23925
24387
  var INTERRUPTED_QUEUE_REASON = "Interrupted before the steer was accepted.";
23926
24388
  async function preserveQueuedFollowUps(manager, threadId) {
23927
24389
  const coordinator = manager.getTurnCoordinator();
23928
- if (!isRecord6(coordinator) || typeof coordinator.loadMessages !== "function" || typeof coordinator.readMessages !== "function" || typeof coordinator.mutate !== "function") {
24390
+ if (!isRecord7(coordinator) || typeof coordinator.loadMessages !== "function" || typeof coordinator.readMessages !== "function" || typeof coordinator.mutate !== "function") {
23929
24391
  throw new Error("Desktop follow-up queue binding is unavailable");
23930
24392
  }
23931
24393
  await coordinator.loadMessages.call(coordinator, threadId);
23932
24394
  const messages = coordinator.readMessages.call(coordinator, threadId);
23933
24395
  if (!Array.isArray(messages)) throw new Error("Desktop follow-up queue is unavailable");
23934
24396
  const alreadyPaused = new Set(
23935
- messages.filter((message2) => isRecord6(message2) && message2.pausedReason != null).map((message2) => message2.id)
24397
+ messages.filter((message2) => isRecord7(message2) && message2.pausedReason != null).map((message2) => message2.id)
23936
24398
  );
23937
24399
  return () => {
23938
24400
  coordinator.mutate.call(coordinator, threadId, (current) => {
23939
24401
  if (!Array.isArray(current)) return current;
23940
24402
  return current.map((message2) => {
23941
- if (!isRecord6(message2) || alreadyPaused.has(message2.id) || message2.pausedReason !== INTERRUPTED_QUEUE_REASON)
24403
+ if (!isRecord7(message2) || alreadyPaused.has(message2.id) || message2.pausedReason !== INTERRUPTED_QUEUE_REASON)
23942
24404
  return message2;
23943
24405
  const resumed = { ...message2 };
23944
24406
  delete resumed.pausedReason;
@@ -23953,21 +24415,25 @@ 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) {
23959
24422
  if ((method === "turn/start" || method === "turn/steer") && prepareTurnInput) {
23960
24423
  params = prepareTurnInput(params);
23961
24424
  }
23962
- const messageId = isRecord6(params) ? params.clientUserMessageId : null;
23963
- const route = typeof messageId === "string" && isRecord6(params) ? routes.get(`${params.threadId}\0${messageId}`) : void 0;
23964
- if (method !== "turn/start" || !isRecord6(params) || !route || route.threadId !== params.threadId) {
24425
+ const messageId = isRecord7(params) ? params.clientUserMessageId : null;
24426
+ const route = typeof messageId === "string" && isRecord7(params) ? routes.get(`${params.threadId}\0${messageId}`) : void 0;
24427
+ if (method !== "turn/start" || !isRecord7(params) || !route || route.threadId !== params.threadId) {
23965
24428
  if (method === "turn/start" && sendTurnStart) {
23966
24429
  return sendTurnStart(params, (input) => {
23967
24430
  if (disposed) throw new Error("External submission binding was disposed");
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,12 +24448,13 @@ 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
  )
23989
24456
  ).then((response) => {
23990
- if (!isRecord6(response) || typeof response.turnId !== "string" || !response.turnId) {
24457
+ if (!isRecord7(response) || typeof response.turnId !== "string" || !response.turnId) {
23991
24458
  throw new Error("External steering returned no replacement Turn identity");
23992
24459
  }
23993
24460
  return {
@@ -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,
@@ -24018,7 +24513,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24018
24513
  ] = args;
24019
24514
  if (typeof threadId !== "string") return originalSteer.apply(manager, args);
24020
24515
  const role = manager.getStreamRole?.(threadId);
24021
- if (isRecord6(role) && role.role === "follower") return originalSteer.apply(manager, args);
24516
+ if (isRecord7(role) && role.role === "follower") return originalSteer.apply(manager, args);
24022
24517
  let host = null;
24023
24518
  let expectedTurnId;
24024
24519
  try {
@@ -24026,36 +24521,31 @@ 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
- if (isRecord6(currentRole) && currentRole.role === "follower")
24540
+ if (isRecord7(currentRole) && currentRole.role === "follower")
24051
24541
  return originalSteer.apply(manager, args);
24052
24542
  if (!Array.isArray(input) || input.length === 0 || input.some(
24053
- (item) => !isRecord6(item) || item.type !== "text" || typeof item.text !== "string"
24054
- ) || !input.some((item) => isRecord6(item) && typeof item.text === "string" && item.text.trim())) {
24543
+ (item) => !isRecord7(item) || item.type !== "text" || typeof item.text !== "string"
24544
+ ) || !input.some((item) => isRecord7(item) && typeof item.text === "string" && item.text.trim())) {
24055
24545
  throw new Error("External steering requires non-empty text input");
24056
24546
  }
24057
24547
  if (toolOutput != null) throw new Error("External steering cannot replace a tool response");
24058
- if (!isRecord6(restoreMessage) || !isRecord6(restoreMessage.context)) {
24548
+ if (!isRecord7(restoreMessage) || !isRecord7(restoreMessage.context)) {
24059
24549
  throw new Error("Desktop steering message context is unavailable");
24060
24550
  }
24061
24551
  const messageId = typeof clientUserMessageId === "string" && clientUserMessageId ? clientUserMessageId : typeof restoreMessage.id === "string" && restoreMessage.id ? restoreMessage.id : crypto.randomUUID();
@@ -24103,7 +24593,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24103
24593
  },
24104
24594
  onMessageAdded
24105
24595
  );
24106
- if (!isRecord6(response) || !isRecord6(response.turn) || typeof response.turn.id !== "string") {
24596
+ if (!isRecord7(response) || !isRecord7(response.turn) || typeof response.turn.id !== "string") {
24107
24597
  throw new Error("Desktop returned no replacement Turn identity");
24108
24598
  }
24109
24599
  try {
@@ -24134,7 +24624,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24134
24624
  }
24135
24625
 
24136
24626
  // src/renderer-remote-attachments.ts
24137
- function isRecord7(value) {
24627
+ function isRecord8(value) {
24138
24628
  return value !== null && typeof value === "object" && !Array.isArray(value);
24139
24629
  }
24140
24630
  var MAX_IMAGE_BYTES = 20 * 1024 * 1024;
@@ -24186,14 +24676,14 @@ Codex Fast mode prioritizes requests and may increase usage.`
24186
24676
  function createRemoteAttachmentSender(target, hostId2, isCurrent) {
24187
24677
  const prepared = /* @__PURE__ */ new WeakMap();
24188
24678
  return (params, send) => {
24189
- if (hostId2 === "local" || !isRecord7(target) || !isRecord7(params) || typeof params.threadId !== "string" || !Array.isArray(params.input) || typeof target.getConversation !== "function")
24679
+ if (hostId2 === "local" || !isRecord8(target) || !isRecord8(params) || typeof params.threadId !== "string" || !Array.isArray(params.input) || typeof target.getConversation !== "function")
24190
24680
  return send(params);
24191
24681
  const images = params.input.filter(
24192
- (item) => isRecord7(item) && (item.type === "image" || item.type === "localImage")
24682
+ (item) => isRecord8(item) && (item.type === "image" || item.type === "localImage")
24193
24683
  );
24194
24684
  if (images.length === 0) return send(params);
24195
24685
  const conversation = target.getConversation.call(target, params.threadId);
24196
- if (!isRecord7(conversation) || conversation.id !== params.threadId || conversation.modelProvider !== "codexhost")
24686
+ if (!isRecord8(conversation) || conversation.id !== params.threadId || conversation.modelProvider !== "codexhost")
24197
24687
  return send(params);
24198
24688
  const assertCurrent = () => {
24199
24689
  if (!isCurrent()) throw new Error("Remote attachment connection is no longer available");
@@ -24206,7 +24696,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24206
24696
  const cached2 = prepared.get(params);
24207
24697
  if (cached2) return cached2.then(dispatch);
24208
24698
  const input = params.input;
24209
- const text = input.filter((item) => isRecord7(item) && item.type === "text").map((item) => isRecord7(item) && typeof item.text === "string" ? item.text : "").join("\n");
24699
+ const text = input.filter((item) => isRecord8(item) && item.type === "text").map((item) => isRecord8(item) && typeof item.text === "string" ? item.text : "").join("\n");
24210
24700
  const existingPaths = nativeImagePaths(text);
24211
24701
  const prepare = async () => {
24212
24702
  if (existingPaths.length === images.length) return params;
@@ -24238,7 +24728,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24238
24728
  contents: file2.contents,
24239
24729
  label: `image-${index + 1}.${file2.extension}`
24240
24730
  });
24241
- if (!isRecord7(saved) || !filePath(saved.path) || saved.fsPath !== saved.path) {
24731
+ if (!isRecord8(saved) || !filePath(saved.path) || saved.fsPath !== saved.path) {
24242
24732
  throw new Error("Desktop returned an invalid remote attachment path");
24243
24733
  }
24244
24734
  created.push(saved.path);
@@ -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);
@@ -24345,11 +24835,11 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
24345
24835
  }
24346
24836
 
24347
24837
  // src/renderer-manual-compaction.ts
24348
- function isRecord8(value) {
24838
+ function isRecord9(value) {
24349
24839
  return value !== null && typeof value === "object" && !Array.isArray(value);
24350
24840
  }
24351
24841
  function installRendererManualCompaction(target, hostId2, messages) {
24352
- if (!messages || typeof messages.addEventListener !== "function" || typeof messages.removeEventListener !== "function" || !isRecord8(target) || typeof target.registerPendingManualContextCompaction !== "function" || typeof target.getConversation !== "function") {
24842
+ if (!messages || typeof messages.addEventListener !== "function" || typeof messages.removeEventListener !== "function" || !isRecord9(target) || typeof target.registerPendingManualContextCompaction !== "function" || typeof target.getConversation !== "function") {
24353
24843
  return null;
24354
24844
  }
24355
24845
  const register = target.registerPendingManualContextCompaction;
@@ -24358,18 +24848,18 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
24358
24848
  const listener = (event) => {
24359
24849
  if (event.source != null && event.source !== messages) return;
24360
24850
  const message2 = event.data;
24361
- if (!isRecord8(message2) || message2.type !== "mcp-notification" || message2.hostId !== hostId2 || message2.method !== THREAD_MANUAL_COMPACTION_STARTED_METHOD) {
24851
+ if (!isRecord9(message2) || message2.type !== "mcp-notification" || message2.hostId !== hostId2 || message2.method !== THREAD_MANUAL_COMPACTION_STARTED_METHOD) {
24362
24852
  return;
24363
24853
  }
24364
24854
  const params = threadManualCompactionStartedSchema.safeParse(message2.params);
24365
24855
  if (!params.success) return;
24366
24856
  const { threadId } = params.data;
24367
24857
  const conversation = getConversation.call(target, threadId);
24368
- if (!isRecord8(conversation) || conversation.id !== threadId || conversation.modelProvider !== "codexhost") {
24858
+ if (!isRecord9(conversation) || conversation.id !== threadId || conversation.modelProvider !== "codexhost") {
24369
24859
  return;
24370
24860
  }
24371
24861
  const role = getStreamRole?.call(target, threadId);
24372
- if (isRecord8(role) && role.role === "follower") return;
24862
+ if (isRecord9(role) && role.role === "follower") return;
24373
24863
  register.call(target, threadId);
24374
24864
  };
24375
24865
  messages.addEventListener("message", listener);
@@ -24584,7 +25074,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
24584
25074
  var SIDEBAR_THREAD_HOST_ID_ATTRIBUTE = "data-app-action-sidebar-thread-host-id";
24585
25075
  var SIDEBAR_AGENT_ICON_ATTRIBUTE = "data-codexhost-sidebar-agent-icon";
24586
25076
  var OWNERSHIP_RETRY_DELAYS_MS = [100, 300, 800, 1500, 3e3];
24587
- function isRecord9(value) {
25077
+ function isRecord10(value) {
24588
25078
  return typeof value === "object" && value !== null && !Array.isArray(value);
24589
25079
  }
24590
25080
  function sidebarThreadAttributes(element) {
@@ -24611,19 +25101,19 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
24611
25101
  const fiberName = fiberNames[0];
24612
25102
  if (fiberNames.length !== 1 || !fiberName) return null;
24613
25103
  const firstFiber2 = Object.getOwnPropertyDescriptor(element, fiberName)?.value;
24614
- if (!isRecord9(firstFiber2)) return null;
25104
+ if (!isRecord10(firstFiber2)) return null;
24615
25105
  const candidates = /* @__PURE__ */ new Set();
24616
25106
  let fiber = firstFiber2;
24617
25107
  for (let depth = 0; fiber && depth < 12; depth += 1) {
24618
25108
  const props = fiber.memoizedProps;
24619
- if (isRecord9(props) && isRecord9(props.dataAttributes)) {
25109
+ if (isRecord10(props) && isRecord10(props.dataAttributes)) {
24620
25110
  const dataAttributes = props.dataAttributes;
24621
25111
  const threadId = hostThreadIdSchema.safeParse(props.conversationId);
24622
25112
  if (threadId.success && dataAttributes[SIDEBAR_THREAD_ROW_ATTRIBUTE] === rowMarker && dataAttributes[SIDEBAR_THREAD_ID_ATTRIBUTE] === taskKey && dataAttributes[SIDEBAR_THREAD_HOST_ID_ATTRIBUTE] === hostId2) {
24623
25113
  candidates.add(threadId.data);
24624
25114
  }
24625
25115
  }
24626
- fiber = isRecord9(fiber.return) ? fiber.return : null;
25116
+ fiber = isRecord10(fiber.return) ? fiber.return : null;
24627
25117
  }
24628
25118
  return candidates.size === 1 ? candidates.values().next().value ?? null : null;
24629
25119
  }
@@ -25033,7 +25523,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25033
25523
  if (row) finish(row);
25034
25524
  });
25035
25525
  }
25036
- function isRecord10(value) {
25526
+ function isRecord11(value) {
25037
25527
  return typeof value === "object" && value !== null && !Array.isArray(value);
25038
25528
  }
25039
25529
  function firstFiber(element) {
@@ -25043,7 +25533,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25043
25533
  const name = names[0];
25044
25534
  if (names.length !== 1 || !name) return null;
25045
25535
  const value = Object.getOwnPropertyDescriptor(element, name)?.value;
25046
- return isRecord10(value) ? value : null;
25536
+ return isRecord11(value) ? value : null;
25047
25537
  }
25048
25538
  function directForkAncestors(first) {
25049
25539
  const ancestors = [];
@@ -25053,7 +25543,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25053
25543
  if (seen.has(fiber)) return [];
25054
25544
  seen.add(fiber);
25055
25545
  ancestors.push(fiber);
25056
- fiber = isRecord10(fiber.return) ? fiber.return : null;
25546
+ fiber = isRecord11(fiber.return) ? fiber.return : null;
25057
25547
  }
25058
25548
  return ancestors;
25059
25549
  }
@@ -25071,7 +25561,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25071
25561
  let hasForkCallback = false;
25072
25562
  for (const fiber of ancestors) {
25073
25563
  const props = fiber.memoizedProps;
25074
- if (!isRecord10(props)) continue;
25564
+ if (!isRecord11(props)) continue;
25075
25565
  if (!hasForkCallback && typeof props.onFork !== "function" && ownsOtherCallback(fiber, props)) {
25076
25566
  return null;
25077
25567
  }
@@ -25103,7 +25593,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25103
25593
  if (!threadId.success || !turnId.success) return null;
25104
25594
  const fiber = firstFiber(button2);
25105
25595
  const buttonProps = fiber?.memoizedProps;
25106
- if (!fiber || !isRecord10(buttonProps) || !Object.hasOwn(buttonProps, "aria-busy")) return null;
25596
+ if (!fiber || !isRecord11(buttonProps) || !Object.hasOwn(buttonProps, "aria-busy")) return null;
25107
25597
  return forkTargetFromAncestors(button2, threadId.data, turnId.data, directForkAncestors(fiber)) ?? forkTargetFromAncestors(button2, threadId.data, turnId.data, committedReactAncestors(fiber));
25108
25598
  }
25109
25599
  var BrowserRendererForkDom = class {
@@ -25193,7 +25683,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25193
25683
  }
25194
25684
  var DRAFT_PREWARM_POLICY_WAIT_TIMEOUT_MS = 1e4;
25195
25685
  var DRAFT_PREWARM_POLICY_POLL_INTERVAL_MS = 25;
25196
- function isRecord11(value) {
25686
+ function isRecord12(value) {
25197
25687
  return typeof value === "object" && value !== null && !Array.isArray(value);
25198
25688
  }
25199
25689
  function decodeOpenCodeTransportModelId(value) {
@@ -25358,7 +25848,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25358
25848
  let fiber = findComposerFiber(composer);
25359
25849
  for (let depth = 0; fiber && depth < 120; depth += 1) {
25360
25850
  const props = fiber.memoizedProps;
25361
- if (isRecord11(props) && "conversationId" in props && props.conversationId != null) {
25851
+ if (isRecord12(props) && "conversationId" in props && props.conversationId != null) {
25362
25852
  const candidate = hostThreadIdSchema.safeParse(props.conversationId);
25363
25853
  if (!candidate.success || threadId !== void 0 && threadId !== candidate.data) {
25364
25854
  return null;
@@ -25371,12 +25861,12 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25371
25861
  return threadId;
25372
25862
  }
25373
25863
  function isLegacySevenSlotDraftWrapper(value) {
25374
- if (!Array.isArray(value) || value.length !== 7 || value[3] !== value[5] || value[3] !== value[6] || !isRecord11(value[3]) || typeof value[3].get !== "function" || typeof value[2] !== "string" && value[2] !== null) {
25864
+ if (!Array.isArray(value) || value.length !== 7 || value[3] !== value[5] || value[3] !== value[6] || !isRecord12(value[3]) || typeof value[3].get !== "function" || typeof value[2] !== "string" && value[2] !== null) {
25375
25865
  return false;
25376
25866
  }
25377
25867
  try {
25378
25868
  const draft = value[3].get();
25379
- return isRecord11(draft) && "modelSettings" in draft && "isManuallyChanged" in draft;
25869
+ return isRecord12(draft) && "modelSettings" in draft && "isManuallyChanged" in draft;
25380
25870
  } catch {
25381
25871
  return false;
25382
25872
  }
@@ -25410,8 +25900,8 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25410
25900
  let fiber = findComposerFiber(composer);
25411
25901
  for (let depth = 0; fiber && depth < 120; depth += 1) {
25412
25902
  const updateQueue = fiber.updateQueue;
25413
- const memoCache = isRecord11(updateQueue) ? updateQueue.memoCache : null;
25414
- const data = isRecord11(memoCache) && Array.isArray(memoCache.data) ? memoCache.data : [];
25903
+ const memoCache = isRecord12(updateQueue) ? updateQueue.memoCache : null;
25904
+ const data = isRecord12(memoCache) && Array.isArray(memoCache.data) ? memoCache.data : [];
25415
25905
  for (const value of data) {
25416
25906
  const draftId = draftIdFromMemoValue(value);
25417
25907
  if (draftId) draftIds.add(draftId);
@@ -25439,7 +25929,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25439
25929
  return ["default", draftIds.values().next().value];
25440
25930
  }
25441
25931
  function isDraftPrewarmPolicyReady(value) {
25442
- return isRecord11(value) && value.state === "ready" && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.select === "function" && typeof value.clear === "function";
25932
+ return isRecord12(value) && value.state === "ready" && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.select === "function" && typeof value.clear === "function";
25443
25933
  }
25444
25934
  async function waitForRendererDraftPrewarmPolicy(target, composer) {
25445
25935
  const deadline = Date.now() + DRAFT_PREWARM_POLICY_WAIT_TIMEOUT_MS;
@@ -25705,11 +26195,11 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25705
26195
  return null;
25706
26196
  }
25707
26197
  }
25708
- function isRecord12(value) {
26198
+ function isRecord13(value) {
25709
26199
  return typeof value === "object" && value !== null && !Array.isArray(value);
25710
26200
  }
25711
26201
  function parseExternalConfiguration(value) {
25712
- if (!isRecord12(value)) return void 0;
26202
+ if (!isRecord13(value)) return void 0;
25713
26203
  const model2 = harnessModelRefSchema.safeParse(value.model);
25714
26204
  if (!model2.success) return void 0;
25715
26205
  const thinkingOptionId = harnessThinkingOptionIdSchema.safeParse(value.thinkingOptionId);
@@ -25726,10 +26216,10 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25726
26216
  const raw = storage.getItem(RENDERER_NEW_THREAD_PREFERENCE_KEY);
25727
26217
  if (!raw) return void 0;
25728
26218
  const parsed = JSON.parse(raw);
25729
- if (!isRecord12(parsed) || parsed.version !== 1) return void 0;
26219
+ if (!isRecord13(parsed) || parsed.version !== 1) return void 0;
25730
26220
  if (parsed.lastAgent !== "codex" && !harnessPluginIdSchema.safeParse(parsed.lastAgent).success)
25731
26221
  return void 0;
25732
- const externalByAgent = isRecord12(parsed.externalByAgent) ? parsed.externalByAgent : {};
26222
+ const externalByAgent = isRecord13(parsed.externalByAgent) ? parsed.externalByAgent : {};
25733
26223
  const parsedExternal = Object.fromEntries(
25734
26224
  Object.keys(externalByAgent).filter((agent) => harnessPluginIdSchema.safeParse(agent).success).flatMap((agent) => {
25735
26225
  const configuration = parseExternalConfiguration(externalByAgent[agent]);
@@ -25855,7 +26345,12 @@ ${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;
25858
- function isRecord13(value) {
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
+ }
26353
+ function isRecord14(value) {
25859
26354
  return typeof value === "object" && value !== null && !Array.isArray(value);
25860
26355
  }
25861
26356
  function canonicalLocale(value) {
@@ -25877,7 +26372,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25877
26372
  return new DOMException("The operation was aborted", "AbortError");
25878
26373
  }
25879
26374
  function parseLocaleOverride(value) {
25880
- if (!isRecord13(value) || !("value" in value)) {
26375
+ if (!isRecord14(value) || !("value" in value)) {
25881
26376
  throw new Error("Codex locale override response is malformed");
25882
26377
  }
25883
26378
  if (value.value === null) return { value: null };
@@ -25892,7 +26387,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25892
26387
  return locale;
25893
26388
  }
25894
26389
  function parseLocaleInfo(value) {
25895
- if (!isRecord13(value)) throw new Error("Codex locale info response is malformed");
26390
+ if (!isRecord14(value)) throw new Error("Codex locale info response is malformed");
25896
26391
  return {
25897
26392
  ideLocale: optionalLocale(value.ideLocale, "IDE locale"),
25898
26393
  systemLocale: optionalLocale(value.systemLocale, "system locale")
@@ -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());
@@ -25921,7 +26416,7 @@ ${text.includes("## My request:") ? "" : "\n## My request:\n"}`,
25921
26416
  const onAbort = () => settle(() => reject(abortError2()));
25922
26417
  const onMessage = (event) => {
25923
26418
  const message3 = event.data;
25924
- if (!isRecord13(message3) || message3.type !== "fetch-response") return;
26419
+ if (!isRecord14(message3) || message3.type !== "fetch-response") return;
25925
26420
  if (message3.requestId !== requestId) return;
25926
26421
  if (message3.responseType !== "success" || typeof message3.status !== "number" || message3.status < 200 || message3.status >= 300 || typeof message3.bodyJsonString !== "string") {
25927
26422
  settle(() => reject(new Error("Codex locale request failed")));
@@ -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",
@@ -31012,10 +31608,10 @@ ${detail}` : ""}`
31012
31608
  let previousWasTranslation = false;
31013
31609
  paragraphLines.forEach((line, lineIndex) => {
31014
31610
  if (lineIndex > 0 && !primaryIsCjk && CJK_PATTERN.test(line)) {
31015
- const translation = document2.createElement("span");
31016
- translation.className = "release-note-translation";
31017
- appendInline(document2, translation, line);
31018
- paragraph.append(translation);
31611
+ const translation2 = document2.createElement("span");
31612
+ translation2.className = "release-note-translation";
31613
+ appendInline(document2, translation2, line);
31614
+ paragraph.append(translation2);
31019
31615
  previousWasTranslation = true;
31020
31616
  return;
31021
31617
  }
@@ -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
  });
@@ -31745,17 +32341,6 @@ ${detail}` : ""}`
31745
32341
  if (periodType === "seven_day") return messages.accountCreditsPeriodSevenDay;
31746
32342
  return messages.accountCreditsPeriodUnknown;
31747
32343
  }
31748
- function creditsProductLabel(product, messages) {
31749
- if (product === "GrokBuild" || product === "Build") return messages.accountCreditsBuild;
31750
- if (product === "5-hour window") return messages.accountCreditsPeriodFiveHour;
31751
- if (product === "7-day window") return messages.accountCreditsPeriodSevenDay;
31752
- if (product.endsWith(" \xB7 monthly"))
31753
- return `${product.slice(0, -" \xB7 monthly".length)} \xB7 ${messages.accountCreditsPeriodMonthly}`;
31754
- if (product === "GrokChat") return "Chat";
31755
- if (product === "GrokImagine") return "Imagine";
31756
- if (product === "GrokVoice") return "Voice";
31757
- return product;
31758
- }
31759
32344
  function formatAccountCreditsReset(value, locale, now = /* @__PURE__ */ new Date()) {
31760
32345
  if (locale !== "zh-CN") return formatRendererCreditsReset(value, now);
31761
32346
  const date5 = new Date(value);
@@ -31782,36 +32367,22 @@ ${detail}` : ""}`
31782
32367
  if (periodType === "weekly" || periodType === "seven_day") return "seven_day";
31783
32368
  return null;
31784
32369
  }
31785
- function scopedUsageProduct(product) {
31786
- const suffixes = [
31787
- [" \xB7 5-hour window", "five_hour"],
31788
- [" \xB7 Weekly window", "seven_day"],
31789
- [" \xB7 7-day window", "seven_day"],
31790
- [" \xB7 5-hour", "five_hour"],
31791
- [" \xB7 7-day", "seven_day"]
31792
- ];
31793
- for (const [suffix, period] of suffixes) {
31794
- if (!product.endsWith(suffix)) continue;
31795
- const scope = product.slice(0, -suffix.length).trim();
31796
- if (scope) return { scope, period };
31797
- }
31798
- return null;
31799
- }
31800
32370
  function splitUsageWindows(credits, messages, filter) {
31801
32371
  const generic = { columns: {} };
31802
32372
  const scopedRows = /* @__PURE__ */ new Map();
31803
32373
  const additional = [];
31804
32374
  const addScoped = (scope, period, window2) => {
31805
- const rows2 = scopedRows.get(scope) ?? [];
32375
+ const scopeLabel = localizeHarnessPresentation(scope, messages.locale).label;
32376
+ const rows2 = scopedRows.get(scope.id) ?? [];
31806
32377
  let row = rows2.find((candidate) => !candidate.columns[period]);
31807
32378
  if (!row) {
31808
- row = { scope, columns: {} };
32379
+ row = { scope: scopeLabel, columns: {} };
31809
32380
  rows2.push(row);
31810
- scopedRows.set(scope, rows2);
32381
+ scopedRows.set(scope.id, rows2);
31811
32382
  }
31812
32383
  row.columns[period] = {
31813
32384
  ...window2,
31814
- label: `${scope} \xB7 ${creditsPeriodLabel2(period, messages)}`,
32385
+ label: window2.label,
31815
32386
  scoped: true
31816
32387
  };
31817
32388
  };
@@ -31821,29 +32392,30 @@ ${detail}` : ""}`
31821
32392
  else additional.push(window2);
31822
32393
  };
31823
32394
  const primary = {
31824
- label: credits.label ? creditsProductLabel(credits.label, messages) : creditsPeriodLabel2(credits.periodType, messages),
32395
+ label: accountCreditsWindowLabel(
32396
+ credits,
32397
+ messages.locale,
32398
+ (period) => creditsPeriodLabel2(period, messages)
32399
+ ),
31825
32400
  usedPercent: credits.usedPercent,
31826
32401
  amount: credits,
31827
32402
  resetsAt: credits.resetsAt,
31828
- scoped: Boolean(credits.label && scopedUsageProduct(credits.label))
32403
+ scoped: Boolean(credits.scope)
31829
32404
  };
31830
- const primaryScope = credits.label ? scopedUsageProduct(credits.label) : null;
31831
- addWindow(
31832
- primary,
31833
- primaryScope?.period ?? (credits.label ? null : comparisonPeriod(credits.periodType)),
31834
- primaryScope?.scope
31835
- );
32405
+ addWindow(primary, comparisonPeriod(credits.periodType), credits.scope);
31836
32406
  for (const product of credits.productUsage ?? []) {
31837
- const scoped = scopedUsageProduct(product.product);
31838
32407
  const window2 = {
31839
- label: creditsProductLabel(product.product, messages),
32408
+ label: accountCreditsWindowLabel(
32409
+ product,
32410
+ messages.locale,
32411
+ (period) => creditsPeriodLabel2(period, messages)
32412
+ ),
31840
32413
  usedPercent: product.usagePercent,
31841
32414
  amount: product,
31842
32415
  resetsAt: product.resetsAt,
31843
- scoped: Boolean(scoped)
32416
+ scoped: Boolean(product.scope)
31844
32417
  };
31845
- const genericPeriod = product.product === "5-hour window" ? "five_hour" : product.product === "7-day window" ? "seven_day" : null;
31846
- addWindow(window2, scoped?.period ?? genericPeriod, scoped?.scope);
32418
+ addWindow(window2, comparisonPeriod(product.periodType ?? "unknown"), product.scope);
31847
32419
  }
31848
32420
  const structured = [...scopedRows.values()].flat();
31849
32421
  let rows = Object.keys(generic.columns).length > 0 ? [generic, ...structured] : structured;
@@ -32235,8 +32807,7 @@ ${detail}` : ""}`
32235
32807
  { status: "ready", credits: account.credits, freshness: "live", observedAt: null },
32236
32808
  messages,
32237
32809
  display,
32238
- () => void 0,
32239
- account.harnessId === "grok" ? "weekly-only" : "all"
32810
+ () => void 0
32240
32811
  ) : renderAccountBalance(document2, messages, account.balance);
32241
32812
  personCell.title = messages.accountNativeManagementHint.replace("{harness}", account.harnessName);
32242
32813
  appendAccountExtras(personCell, [importAction]);
@@ -33202,7 +33773,7 @@ ${detail}` : ""}`
33202
33773
  }
33203
33774
  });
33204
33775
  }
33205
- 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) {
33206
33777
  return Object.freeze([
33207
33778
  createConnectionsSettingsPage(messages, getDiagnostics, void 0, getRemoteConnections),
33208
33779
  createRemoteConnectionsPage(messages, getRemoteConnections),
@@ -33216,14 +33787,15 @@ ${detail}` : ""}`
33216
33787
  createUsageStatisticsPage(
33217
33788
  consoleMessages(messages.locale),
33218
33789
  requestUsageStatistics,
33219
- messages.locale
33790
+ messages.locale,
33791
+ usageStatisticsHosts
33220
33792
  ),
33221
33793
  createAppearanceSettingsPage(messages, getLoadedSessionsClient),
33222
33794
  updatesPage(messages, getUpdateClient),
33223
33795
  aboutPage(messages, getUpdateClient)
33224
33796
  ]);
33225
33797
  }
33226
- 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) {
33227
33799
  return createRendererSettingsPageRegistry(
33228
33800
  createDefaultRendererSettingsPages(
33229
33801
  messages,
@@ -33234,7 +33806,8 @@ ${detail}` : ""}`
33234
33806
  openImportedThread,
33235
33807
  void 0,
33236
33808
  void 0,
33237
- requestUsageStatistics
33809
+ requestUsageStatistics,
33810
+ usageStatisticsHosts
33238
33811
  )
33239
33812
  );
33240
33813
  }
@@ -34008,6 +34581,20 @@ ${detail}` : ""}`
34008
34581
  gap: 18px;
34009
34582
  }
34010
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
+
34011
34598
  .settings-connection-list,
34012
34599
  .settings-connection-inspector {
34013
34600
  overflow: hidden;
@@ -34016,14 +34603,36 @@ ${detail}` : ""}`
34016
34603
  border-radius: 10px;
34017
34604
  }
34018
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
+
34019
34628
  .settings-connection-host-strip {
34020
34629
  display: grid;
34630
+ flex: 1;
34021
34631
  grid-template-columns: 30px minmax(0, 1fr) 30px;
34022
34632
  align-items: center;
34023
34633
  min-width: 0;
34024
34634
  gap: 5px;
34025
34635
  padding: 9px 10px;
34026
- border-bottom: 1px solid var(--settings-divider);
34027
34636
  }
34028
34637
 
34029
34638
  .settings-connection-host-scroll {
@@ -34120,7 +34729,7 @@ ${detail}` : ""}`
34120
34729
  .settings-connection-table-header,
34121
34730
  .settings-connection-row {
34122
34731
  display: grid;
34123
- grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 108px;
34732
+ grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) minmax(108px, max-content);
34124
34733
  align-items: center;
34125
34734
  min-width: 0;
34126
34735
  gap: 10px;
@@ -34213,6 +34822,14 @@ ${detail}` : ""}`
34213
34822
  line-height: 18px;
34214
34823
  }
34215
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
+
34216
34833
  .settings-connection-row__status::before {
34217
34834
  width: 6px;
34218
34835
  height: 6px;
@@ -34249,6 +34866,7 @@ ${detail}` : ""}`
34249
34866
  justify-content: flex-end;
34250
34867
  min-width: 0;
34251
34868
  gap: 4px;
34869
+ white-space: nowrap;
34252
34870
  }
34253
34871
 
34254
34872
  .settings-connection-row__handle {
@@ -38406,7 +39024,8 @@ ${usage_default}`;
38406
39024
  },
38407
39025
  options.getLoadedSessionsClient ?? (() => null),
38408
39026
  options.getRemoteConnections ?? (() => null),
38409
- options.requestUsageStatistics
39027
+ options.requestUsageStatistics,
39028
+ options.usageStatisticsHosts
38410
39029
  );
38411
39030
  const nextShell = installRendererSettingsShell(definitions, messages, ownerWindow.document, {
38412
39031
  onOpenChange(open) {
@@ -38555,6 +39174,30 @@ ${usage_default}`;
38555
39174
  };
38556
39175
  }
38557
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
+
38558
39201
  // src/renderer-native-composer-controller.ts
38559
39202
  var MAX_ELEMENT_DEPTH = 4;
38560
39203
  var MAX_FIBER_DEPTH = 40;
@@ -39147,12 +39790,8 @@ ${usage_default}`;
39147
39790
  const index = Math.max(0, Math.min(Math.trunc(attempt), rendererUsageRefreshDelays.length - 1));
39148
39791
  return rendererUsageRefreshDelays[index] ?? rendererUsageRefreshDelays[0];
39149
39792
  }
39150
- function externalAgentHasAccountCredits(agent) {
39151
- return agent === "codex" || agent === "grok" || agent === "claude-code";
39152
- }
39153
- function shouldRetryExternalThreadUsage(agent, usage, accountCredits = null) {
39154
- if (usage === null) return true;
39155
- return externalAgentHasAccountCredits(agent) && accountCredits === null;
39793
+ function shouldRetryExternalThreadUsage(accountCreditsSupported, usage, accountCredits = null) {
39794
+ return usage === null || accountCreditsSupported && accountCredits === null;
39156
39795
  }
39157
39796
  function shouldReloadExternalCatalogAfterAvailabilityRefresh(previous, next, configurationReady, explicitRefresh = false) {
39158
39797
  const initialDiscoveryReady = previous === "checking" && next === "ready";
@@ -39487,17 +40126,19 @@ ${usage_default}`;
39487
40126
  });
39488
40127
  let connectionDiagnostics = null;
39489
40128
  const remoteConnections = createRemoteConnectionsControl(window, modelClientForHost);
40129
+ const usageStatistics = createRendererUsageStatisticsSource({
40130
+ routing: () => window.__codexhostHostRoutingV1,
40131
+ listConnections: () => remoteConnections.ssh.list(),
40132
+ disposed: () => disposed
40133
+ });
39490
40134
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
39491
40135
  getRemoteConnections: () => remoteConnections,
39492
40136
  getUpdateClient: () => modelControl,
39493
40137
  getAccountClient: () => modelControl,
39494
40138
  getConnectionDiagnostics: () => connectionDiagnostics,
39495
40139
  getLoadedSessionsClient: () => modelClientForHost("local"),
39496
- async requestUsageStatistics(method, params) {
39497
- const route = window.__codexhostHostRoutingV1?.forHost("local");
39498
- if (disposed || !route) throw new Error("Local Host connection is unavailable");
39499
- return route.manager.sendRequest(method, params);
39500
- },
40140
+ requestUsageStatistics: usageStatistics.request,
40141
+ usageStatisticsHosts: usageStatistics.hosts,
39501
40142
  getSessionImportClient: () => {
39502
40143
  const client = modelClientForHost("local");
39503
40144
  const sources = client?.listSessionImportSources;
@@ -39594,9 +40235,10 @@ ${usage_default}`;
39594
40235
  };
39595
40236
  const notifySubmission = (composer, trigger) => {
39596
40237
  const state = controller.recordSubmission(composer);
40238
+ const createsThread = state.phase === "draft";
39597
40239
  const preferenceStorage = rendererNewThreadPreferenceStorage(activeModelHostId(composer));
39598
- writeNewThreadAgentPreference(state.agent, preferenceStorage);
39599
- if (state.agent !== "codex") {
40240
+ if (createsThread) writeNewThreadAgentPreference(state.agent, preferenceStorage);
40241
+ if (createsThread && state.agent !== "codex") {
39600
40242
  const model2 = controller.modelForAgent(composer, state.agent);
39601
40243
  if (model2) {
39602
40244
  writeNewThreadExternalConfigurationPreference(
@@ -39737,6 +40379,8 @@ ${usage_default}`;
39737
40379
  mounted.usageRequestGeneration += 1;
39738
40380
  mounted.usage = update.usage;
39739
40381
  mounted.accountCredits = update.accountCredits ?? null;
40382
+ if (update.accountCreditsSupported !== void 0)
40383
+ mounted.accountCreditsSupported = update.accountCreditsSupported;
39740
40384
  usageRefreshAttempts.delete(mounted.composer);
39741
40385
  renderMounted(mounted);
39742
40386
  }
@@ -39791,13 +40435,13 @@ ${usage_default}`;
39791
40435
  }
39792
40436
  mounted.usage = result.usage;
39793
40437
  mounted.accountCredits = result.accountCredits ?? null;
39794
- const agent = controller.get(mounted.composer).agent;
39795
- if (result.usage !== null && (!externalAgentHasAccountCredits(agent) || result.accountCredits)) {
40438
+ mounted.accountCreditsSupported = result.accountCreditsSupported ?? false;
40439
+ if (result.usage !== null && (!mounted.accountCreditsSupported || result.accountCredits)) {
39796
40440
  usageRefreshAttempts.delete(mounted.composer);
39797
40441
  }
39798
40442
  renderMounted(mounted);
39799
40443
  if (shouldRetryExternalThreadUsage(
39800
- controller.get(mounted.composer).agent,
40444
+ mounted.accountCreditsSupported,
39801
40445
  result.usage,
39802
40446
  result.accountCredits ?? null
39803
40447
  )) {
@@ -39806,7 +40450,7 @@ ${usage_default}`;
39806
40450
  } catch (error51) {
39807
40451
  if (mountedByComposer.get(mounted.composer) === mounted && mounted.usageRequestGeneration === generation) {
39808
40452
  renderMounted(mounted);
39809
- if (!(error51 instanceof RendererMethodUnavailableError) && shouldRetryExternalThreadUsage(controller.get(mounted.composer).agent, null, null)) {
40453
+ if (!(error51 instanceof RendererMethodUnavailableError) && shouldRetryExternalThreadUsage(mounted.accountCreditsSupported, null, null)) {
39810
40454
  scheduleThreadUsageRefresh(mounted);
39811
40455
  }
39812
40456
  }
@@ -39914,14 +40558,7 @@ ${usage_default}`;
39914
40558
  renderMounted(mounted);
39915
40559
  if (mounted.ownershipStatus !== "error") void refreshCommands(mounted);
39916
40560
  sidebarAgentIcons.refresh();
39917
- if (mounted.ownershipStatus !== "error") {
39918
- const agent = controller.get(mounted.composer).agent;
39919
- if (agent === "codex") {
39920
- void refreshThreadUsage(mounted);
39921
- } else if (shouldRetryExternalThreadUsage(agent, mounted.usage, mounted.accountCredits)) {
39922
- scheduleThreadUsageRefresh(mounted);
39923
- }
39924
- }
40561
+ if (mounted.ownershipStatus !== "error") void refreshThreadUsage(mounted);
39925
40562
  } else if (isRetainedComposer(mounted) && controller.isCurrentOwnershipRequest(mounted.composer, generation) && orbitComposerKind(mounted.composer) === "unknown") {
39926
40563
  ownershipRecovery.set(mounted, generation);
39927
40564
  scheduleScan();
@@ -39951,7 +40588,7 @@ ${usage_default}`;
39951
40588
  if (resolution === "transfer") {
39952
40589
  mounted.ownershipStatus = "ready";
39953
40590
  renderMounted(mounted);
39954
- if (shouldRetryExternalThreadUsage(controller.get(mounted.composer).agent, null, null)) {
40591
+ if (shouldRetryExternalThreadUsage(mounted.accountCreditsSupported, null, null)) {
39955
40592
  scheduleThreadUsageRefresh(mounted);
39956
40593
  }
39957
40594
  } else {
@@ -40740,7 +41377,7 @@ ${usage_default}`;
40740
41377
  let webUiAvailable = false;
40741
41378
  try {
40742
41379
  const inspection = await client.inspectHarness(
40743
- { harnessId: harnessIdSchema.parse(agent), refresh },
41380
+ { harnessId: harnessIdSchema.parse(agent), refresh, purpose: "availability" },
40744
41381
  { priority: "background" }
40745
41382
  );
40746
41383
  status = inspection.status === "ready" ? "ready" : inspection.status;
@@ -41042,6 +41679,7 @@ ${usage_default}`;
41042
41679
  threadConfiguration: inherited?.threadConfiguration,
41043
41680
  usage: inherited?.usage ?? null,
41044
41681
  accountCredits: inherited?.accountCredits ?? null,
41682
+ accountCreditsSupported: inherited?.accountCreditsSupported ?? false,
41045
41683
  hostId: inherited?.hostId ?? hostId2,
41046
41684
  usageRequestGeneration: 0,
41047
41685
  commandRequestGeneration: 0
@@ -41054,7 +41692,11 @@ ${usage_default}`;
41054
41692
  sidebarAgentIcons.refresh();
41055
41693
  if (threadIdFromComposerModelTarget(modelTarget) && !inherited) {
41056
41694
  void loadThreadOwnership(mounted);
41057
- } else if (threadIdFromComposerModelTarget(modelTarget) && inherited && shouldRetryExternalThreadUsage(state.agent, mounted.usage, mounted.accountCredits)) {
41695
+ } else if (threadIdFromComposerModelTarget(modelTarget) && inherited && shouldRetryExternalThreadUsage(
41696
+ mounted.accountCreditsSupported,
41697
+ mounted.usage,
41698
+ mounted.accountCredits
41699
+ )) {
41058
41700
  scheduleThreadUsageRefresh(mounted);
41059
41701
  } else if (state.agent !== "codex" && !isExternalConfigurationStable(mounted.modelView, mounted.permissionModeView)) {
41060
41702
  void loadExternalCatalog(mounted);