dsh-codex-community 0.0.1 → 0.0.2

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.
@@ -7,6 +7,7 @@ window.__ModuleLoader__.load({
7
7
  const h = React.createElement
8
8
 
9
9
  const CHANNEL = "/dsh-codex"
10
+ const SESSION_CHANNEL = "/dsh-codex-session"
10
11
  const NS = "settings.codex"
11
12
  const CODEX_PROVIDER = "dsh-codex"
12
13
  const CODEX_SETTINGS_NS = "dsh-codex"
@@ -47,10 +48,21 @@ window.__ModuleLoader__.load({
47
48
  logoutFailed: "退出登录失败,请重试。",
48
49
  responseFailed: "提交登录信息失败,请重试。",
49
50
  requestFailed: "暂时无法连接本机授权服务。",
51
+ locale: "zh-CN",
50
52
  quotaTitle: "使用额度",
51
53
  quotaProduct: "Codex",
52
- quotaUnknown: "尚无可验证的额度观测。",
53
- quotaUnknownHelp: "完成一次 Codex 请求后,此处会更新可验证状态;插件不会调用未公开的账户额度接口。",
54
+ quotaLoading: "正在读取账户额度…",
55
+ quotaLoadFailed: "暂时无法刷新账户额度。",
56
+ quotaStaleHelp: "继续显示上一次验证成功的数据。",
57
+ quotaUnknown: "尚无可验证的额度数据。",
58
+ quotaUnknownHelp: "登录后会在每次进入此页面和手动刷新时读取;读取失败时保留最近的安全观测。",
59
+ quotaFiveHour: "5 小时额度",
60
+ quotaWeekly: "每周额度",
61
+ quotaMinuteWindow: "分钟额度",
62
+ quotaRemaining: "剩余",
63
+ quotaUsed: "已使用",
64
+ quotaResetsAt: "重置时间",
65
+ quotaUpdatedAt: "数据更新时间",
54
66
  quotaRecentSuccess: "最近一次 Codex 请求成功",
55
67
  quotaSuccessCaution: "这只表示最近一次请求成功,不代表账户剩余额度。",
56
68
  quotaExhausted: "最近观测到 Codex 账户额度已用尽。",
@@ -59,8 +71,10 @@ window.__ModuleLoader__.load({
59
71
  quotaResetIn: "预计还有约",
60
72
  quotaMinutes: "分钟",
61
73
  quotaNoReset: "未获得通过校验的重置时间;状态会在有限时间后自动回到未知。",
62
- modelsTitle: "模型选择器中显示的模型",
63
- modelsDescription: "选择要在模型选择器中显示的 Codex 模型。隐藏后,已有会话中保存的精确模型仍可继续使用。",
74
+ modelsTitle: "当前安装的 Codex 模型",
75
+ modelsDescription: "选择要在模型选择器中显示的 Codex 模型。当前目录条目的名称、上下文窗口和最大输出直接来自当前安装的 Codex provider catalog,不推测目录未提供的能力;隐藏后,已有会话中保存的精确模型仍可继续使用。",
76
+ modelsContextWindow: "上下文",
77
+ modelsMaxOutput: "最大输出",
64
78
  modelsLoading: "正在读取 Codex 模型目录…",
65
79
  modelsUnavailable: "当前环境没有可用的 Codex 模型管理接口。",
66
80
  modelsReadOnly: "当前设置存储为只读,无法修改启用模型。",
@@ -74,6 +88,16 @@ window.__ModuleLoader__.load({
74
88
  modelsRetry: "重新读取",
75
89
  modelsLoadFailed: "读取 Codex 模型目录失败。",
76
90
  modelsSaveFailed: "保存模型启用设置失败。",
91
+ fastEnable: "启用 Fast(1.5×)",
92
+ fastDisable: "关闭 Fast,恢复默认速度",
93
+ fastUnsupported: "当前模型不支持 Fast(1.5×)",
94
+ fastLoading: "正在读取 Fast 状态",
95
+ fastUnavailable: "暂时无法读取 Fast 状态,点击重试",
96
+ legacyReasoningRepair: "修复并设为新会话默认模型",
97
+ legacyReasoningRepairing: "正在修复…",
98
+ legacyReasoningRepaired: "已修复并设为新会话默认模型",
99
+ legacyReasoningRepairRetry: "重试修复并设为新会话默认模型",
100
+ legacyReasoningRepairHelp: "旧会话保存了当前不支持的 Off/Minimal。点击后会切换到此模型当前默认档位;与模型选择器相同,此操作也会把该模型保存为未来新会话的默认模型。",
77
101
  }
78
102
  const en = {
79
103
  nav: "OpenAI Codex",
@@ -108,10 +132,21 @@ window.__ModuleLoader__.load({
108
132
  logoutFailed: "Could not sign out. Please try again.",
109
133
  responseFailed: "Could not submit the sign-in response. Please try again.",
110
134
  requestFailed: "The local authorization service is temporarily unavailable.",
135
+ locale: "en-US",
111
136
  quotaTitle: "Usage",
112
137
  quotaProduct: "Codex",
113
- quotaUnknown: "No verifiable quota observation yet.",
114
- quotaUnknownHelp: "This area updates after a Codex request; the plugin does not call undocumented account-quota endpoints.",
138
+ quotaLoading: "Reading account usage…",
139
+ quotaLoadFailed: "Account usage could not be refreshed.",
140
+ quotaStaleHelp: "The last successfully verified data remains visible.",
141
+ quotaUnknown: "No verifiable usage data is available yet.",
142
+ quotaUnknownHelp: "After sign-in, usage is read whenever this page opens and when you refresh it; the latest safe observation remains available if the read fails.",
143
+ quotaFiveHour: "5-hour limit",
144
+ quotaWeekly: "Weekly limit",
145
+ quotaMinuteWindow: "minute limit",
146
+ quotaRemaining: "Remaining",
147
+ quotaUsed: "Used",
148
+ quotaResetsAt: "Resets",
149
+ quotaUpdatedAt: "Data updated",
115
150
  quotaRecentSuccess: "The latest Codex request succeeded",
116
151
  quotaSuccessCaution: "This only describes the latest request; it does not represent remaining account quota.",
117
152
  quotaExhausted: "Codex account quota was exhausted in the latest observation.",
@@ -120,8 +155,10 @@ window.__ModuleLoader__.load({
120
155
  quotaResetIn: "Approximately",
121
156
  quotaMinutes: "minutes remaining",
122
157
  quotaNoReset: "No reset time passed validation; this state automatically returns to unknown after a bounded interval.",
123
- modelsTitle: "Models shown in the model selector",
124
- modelsDescription: "Choose which Codex models appear in the model selector. Hidden models remain available to existing conversations that saved an exact model.",
158
+ modelsTitle: "Codex models in this installation",
159
+ modelsDescription: "Choose which Codex models appear in the model selector. For current catalog entries, names, context windows, and maximum output values come directly from the installed Codex provider catalog; capabilities omitted by the catalog are not inferred. Hidden models remain available to existing conversations that saved an exact model.",
160
+ modelsContextWindow: "Context",
161
+ modelsMaxOutput: "Max output",
125
162
  modelsLoading: "Reading the Codex model catalog…",
126
163
  modelsUnavailable: "Codex model management is unavailable in this environment.",
127
164
  modelsReadOnly: "The settings store is read-only, so enabled models cannot be changed.",
@@ -135,6 +172,16 @@ window.__ModuleLoader__.load({
135
172
  modelsRetry: "Reload",
136
173
  modelsLoadFailed: "Could not read the Codex model catalog.",
137
174
  modelsSaveFailed: "Could not save the enabled models.",
175
+ fastEnable: "Enable Fast (1.5×)",
176
+ fastDisable: "Turn off Fast and restore standard speed",
177
+ fastUnsupported: "The current model does not support Fast (1.5×)",
178
+ fastLoading: "Reading Fast status",
179
+ fastUnavailable: "Fast status is temporarily unavailable; click to retry",
180
+ legacyReasoningRepair: "Repair and set as future default",
181
+ legacyReasoningRepairing: "Repairing…",
182
+ legacyReasoningRepaired: "Repaired and set as future default",
183
+ legacyReasoningRepairRetry: "Retry repair and set as future default",
184
+ legacyReasoningRepairHelp: "This conversation saved an unsupported Off/Minimal level. Click to switch to this model's current default. Like using the model selector, this also saves it as the default model for future conversations.",
138
185
  }
139
186
 
140
187
  function createAuthorizationClient(connection) {
@@ -164,9 +211,199 @@ window.__ModuleLoader__.load({
164
211
  }),
165
212
  cancel: (attemptId) => call("cancel", attemptId === undefined ? {} : { attemptId }),
166
213
  logout: () => call("logout", {}),
214
+ usage: (signal) => call("usage", {}, signal),
167
215
  })
168
216
  }
169
217
 
218
+ function createSessionPreferenceClient(connection) {
219
+ const call = async (endpoint, payload, signal) => {
220
+ const result = await connection.rpc.call(SESSION_CHANNEL, endpoint, payload, signal)
221
+ if (!result.ok) {
222
+ const error = new Error(result.error.message)
223
+ error.code = result.error.code
224
+ throw error
225
+ }
226
+ return result.value
227
+ }
228
+ return Object.freeze({
229
+ forSession(sessionId) {
230
+ return Object.freeze({
231
+ get: (signal) => call("get", { sessionId }, signal),
232
+ setFast: (fast, signal) => call("set-fast", { sessionId, fast }, signal),
233
+ })
234
+ },
235
+ })
236
+ }
237
+
238
+ function supportsFastModel(modelId) {
239
+ return modelId === "gpt-5.4"
240
+ || modelId === "gpt-5.5"
241
+ || modelId === "gpt-5.6-luna"
242
+ || modelId === "gpt-5.6-sol"
243
+ || modelId === "gpt-5.6-terra"
244
+ }
245
+
246
+ function CodexFastToggle({
247
+ session,
248
+ useModelDirectory,
249
+ preferenceClient,
250
+ selectModel,
251
+ t,
252
+ }) {
253
+ const current = useModelDirectory((state) => state.current ?? null)
254
+ const [preference, setPreference] = React.useState({ status: "loading", fast: false })
255
+ const [repair, setRepair] = React.useState({ key: null, status: "idle" })
256
+ const mountedRef = React.useRef(false)
257
+ const generationRef = React.useRef(0)
258
+ const controllerRef = React.useRef(null)
259
+ const repairAttemptRef = React.useRef(null)
260
+ const isCodex = current?.provider === CODEX_PROVIDER
261
+ const supported = isCodex && supportsFastModel(current?.model)
262
+
263
+ const refreshPreference = React.useCallback(async () => {
264
+ if (!mountedRef.current || !isCodex) return
265
+ const generation = ++generationRef.current
266
+ controllerRef.current?.abort()
267
+ const controller = new AbortController()
268
+ controllerRef.current = controller
269
+ setPreference((value) => ({ status: "loading", fast: value.fast }))
270
+ try {
271
+ const value = await preferenceClient.get(controller.signal)
272
+ if (!mountedRef.current || generation !== generationRef.current || controller.signal.aborted) return
273
+ setPreference({ status: "ready", fast: value?.fast === true })
274
+ } catch {
275
+ if (!mountedRef.current || generation !== generationRef.current || controller.signal.aborted) return
276
+ setPreference((value) => ({ status: "error", fast: value.fast }))
277
+ } finally {
278
+ if (controllerRef.current === controller) controllerRef.current = null
279
+ }
280
+ }, [isCodex, preferenceClient])
281
+
282
+ React.useEffect(() => {
283
+ mountedRef.current = true
284
+ if (isCodex) void refreshPreference()
285
+ return () => {
286
+ mountedRef.current = false
287
+ generationRef.current += 1
288
+ controllerRef.current?.abort()
289
+ controllerRef.current = null
290
+ }
291
+ }, [isCodex, preferenceClient, refreshPreference])
292
+
293
+ const legacySelection = isCodex
294
+ && (current?.reasoningEffort === "off" || current?.reasoningEffort === "minimal")
295
+ const legacyKey = legacySelection ? `${current.model}:${current.reasoningEffort}` : null
296
+ const repairStatus = repair.key === legacyKey ? repair.status : "idle"
297
+
298
+ const repairLegacySelection = async () => {
299
+ if (
300
+ !mountedRef.current
301
+ || !legacySelection
302
+ || typeof selectModel !== "function"
303
+ || session?.removed === true
304
+ || repairStatus === "saving"
305
+ || repairStatus === "done"
306
+ || repairAttemptRef.current === legacyKey
307
+ ) return
308
+ const key = legacyKey
309
+ repairAttemptRef.current = key
310
+ setRepair({ key, status: "saving" })
311
+ try {
312
+ await selectModel({
313
+ provider: CODEX_PROVIDER,
314
+ model: current.model,
315
+ })
316
+ if (mountedRef.current) setRepair({ key, status: "done" })
317
+ } catch {
318
+ if (repairAttemptRef.current === key) repairAttemptRef.current = null
319
+ if (mountedRef.current) setRepair({ key, status: "error" })
320
+ }
321
+ }
322
+
323
+ const toggleFast = async () => {
324
+ if (!mountedRef.current || !supported || session?.removed === true) return
325
+ if (preference.status === "error") {
326
+ void refreshPreference()
327
+ return
328
+ }
329
+ if (preference.status !== "ready") return
330
+ const generation = ++generationRef.current
331
+ controllerRef.current?.abort()
332
+ const controller = new AbortController()
333
+ controllerRef.current = controller
334
+ const nextFast = !preference.fast
335
+ setPreference({ status: "saving", fast: preference.fast })
336
+ try {
337
+ const value = await preferenceClient.setFast(nextFast, controller.signal)
338
+ if (!mountedRef.current || generation !== generationRef.current || controller.signal.aborted) return
339
+ setPreference({ status: "ready", fast: value?.fast === true })
340
+ } catch {
341
+ if (!mountedRef.current || generation !== generationRef.current || controller.signal.aborted) return
342
+ setPreference({ status: "error", fast: preference.fast })
343
+ } finally {
344
+ if (controllerRef.current === controller) controllerRef.current = null
345
+ }
346
+ }
347
+
348
+ if (!isCodex) return null
349
+ const label = !supported
350
+ ? t("fastUnsupported")
351
+ : preference.status === "loading" || preference.status === "saving"
352
+ ? t("fastLoading")
353
+ : preference.status === "error"
354
+ ? t("fastUnavailable")
355
+ : preference.fast
356
+ ? t("fastDisable")
357
+ : t("fastEnable")
358
+ const disabled = !supported
359
+ || session?.removed === true
360
+ || preference.status === "loading"
361
+ || preference.status === "saving"
362
+
363
+ const fastButton = h("button", {
364
+ type: "button",
365
+ className: "dshCodexFastToggle",
366
+ "data-fast": preference.fast ? "true" : undefined,
367
+ "aria-label": label,
368
+ "aria-pressed": preference.fast,
369
+ "aria-busy": preference.status === "loading" || preference.status === "saving" ? "true" : undefined,
370
+ title: label,
371
+ disabled,
372
+ onClick: () => void toggleFast(),
373
+ }, h("svg", {
374
+ viewBox: "0 0 24 24",
375
+ width: 16,
376
+ height: 16,
377
+ fill: "none",
378
+ stroke: "currentColor",
379
+ strokeWidth: 2,
380
+ strokeLinecap: "round",
381
+ strokeLinejoin: "round",
382
+ "aria-hidden": "true",
383
+ }, h("path", { d: "M13 2 4.5 13h7L11 22l8.5-11h-7L13 2Z" })))
384
+
385
+ if (!legacySelection || typeof selectModel !== "function") return fastButton
386
+ const repairLabel = repairStatus === "saving"
387
+ ? t("legacyReasoningRepairing")
388
+ : repairStatus === "done"
389
+ ? t("legacyReasoningRepaired")
390
+ : repairStatus === "error"
391
+ ? t("legacyReasoningRepairRetry")
392
+ : t("legacyReasoningRepair")
393
+ return h(React.Fragment, null,
394
+ h("button", {
395
+ type: "button",
396
+ className: "dshCodexLegacyRepair",
397
+ "data-status": repairStatus,
398
+ "aria-label": repairLabel,
399
+ "aria-busy": repairStatus === "saving" ? "true" : undefined,
400
+ title: t("legacyReasoningRepairHelp"),
401
+ disabled: session?.removed === true || repairStatus === "saving" || repairStatus === "done",
402
+ onClick: () => void repairLegacySelection(),
403
+ }, repairLabel),
404
+ fastButton)
405
+ }
406
+
170
407
  function resultValue(response) {
171
408
  if (response?.result?.ok === true) return response.result.value
172
409
  const message = response?.result?.error?.message
@@ -193,6 +430,102 @@ window.__ModuleLoader__.load({
193
430
  }
194
431
  }
195
432
 
433
+ function safeAccountUsage(value) {
434
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined
435
+ if (!Number.isSafeInteger(value.observedAt) || value.observedAt < 0) return undefined
436
+ if (!Array.isArray(value.rateLimits) || value.rateLimits.length > 16) return undefined
437
+ const rateLimits = []
438
+ for (const candidate of value.rateLimits) {
439
+ if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) continue
440
+ if (typeof candidate.limitId !== "string" || candidate.limitId.length < 1 || candidate.limitId.length > 128) continue
441
+ const primary = safeUsageWindow(candidate.primary)
442
+ const secondary = safeUsageWindow(candidate.secondary)
443
+ if (primary === undefined && secondary === undefined) continue
444
+ rateLimits.push({
445
+ limitId: candidate.limitId,
446
+ ...(typeof candidate.limitName === "string" && candidate.limitName.length > 0 && candidate.limitName.length <= 256
447
+ ? { limitName: candidate.limitName }
448
+ : {}),
449
+ ...(primary === undefined ? {} : { primary }),
450
+ ...(secondary === undefined ? {} : { secondary }),
451
+ })
452
+ }
453
+ if (rateLimits.length === 0) return undefined
454
+ return { observedAt: value.observedAt, rateLimits }
455
+ }
456
+
457
+ function safeUsageWindow(value) {
458
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined
459
+ if (typeof value.usedPercent !== "number" || !Number.isFinite(value.usedPercent) || value.usedPercent < 0 || value.usedPercent > 100) return undefined
460
+ if (!Number.isSafeInteger(value.windowDurationMins) || value.windowDurationMins < 1 || value.windowDurationMins > 525_600) return undefined
461
+ if (!Number.isSafeInteger(value.resetsAt) || value.resetsAt < 0) return undefined
462
+ return {
463
+ usedPercent: value.usedPercent,
464
+ windowDurationMins: value.windowDurationMins,
465
+ resetsAt: value.resetsAt,
466
+ }
467
+ }
468
+
469
+ function percentText(value) {
470
+ return String(Math.round(value * 10) / 10)
471
+ }
472
+
473
+ function usageWindowLabel(window, t) {
474
+ if (window.windowDurationMins === 300) return t("quotaFiveHour")
475
+ if (window.windowDurationMins === 10_080) return t("quotaWeekly")
476
+ return `${window.windowDurationMins} ${t("quotaMinuteWindow")}`
477
+ }
478
+
479
+ function renderObservedQuota(quota, t) {
480
+ if (quota.status === "recent-success") {
481
+ return h(React.Fragment, null,
482
+ h("p", { className: "dshCodexQuotaState" }, `${t("quotaRecentSuccess")} · ${new Date(quota.observedAt).toLocaleString(t("locale"))}`),
483
+ h("p", { className: "dshCodexMuted" }, t("quotaSuccessCaution")))
484
+ }
485
+ if (quota.status === "exhausted") {
486
+ return h(React.Fragment, null,
487
+ h("p", { className: "dshCodexQuotaState dshCodexQuotaExhausted" }, t("quotaExhausted")),
488
+ h("p", { className: "dshCodexMuted" }, `${t("quotaObservedAt")} · ${new Date(quota.observedAt).toLocaleString(t("locale"))}`),
489
+ quota.resetAt === undefined
490
+ ? h("p", { className: "dshCodexMuted" }, t("quotaNoReset"))
491
+ : h("p", { className: "dshCodexMuted" }, `${t("quotaResetAt")} · ${new Date(quota.resetAt).toLocaleString(t("locale"))} · ${t("quotaResetIn")} ${quota.remainingMinutes} ${t("quotaMinutes")}`))
492
+ }
493
+ return h(React.Fragment, null,
494
+ h("p", { className: "dshCodexQuotaState dshCodexQuotaUnknown" }, t("quotaUnknown")),
495
+ h("p", { className: "dshCodexMuted" }, t("quotaUnknownHelp")))
496
+ }
497
+
498
+ function renderAccountUsage(usage, t) {
499
+ return h(React.Fragment, null,
500
+ ...usage.rateLimits.map((limit) => {
501
+ const windows = [limit.primary, limit.secondary].filter((window) => window !== undefined)
502
+ const limitLabel = limit.limitId === "codex"
503
+ ? t("quotaProduct")
504
+ : limit.limitName ?? limit.limitId
505
+ return h("section", { key: limit.limitId, className: "dshCodexQuotaGroup" },
506
+ h("p", { className: "dshCodexQuotaProduct" }, limitLabel),
507
+ ...windows.map((window) => {
508
+ const remaining = Math.max(0, Math.min(100, 100 - window.usedPercent))
509
+ const remainingText = percentText(remaining)
510
+ const usedText = percentText(window.usedPercent)
511
+ const label = usageWindowLabel(window, t)
512
+ return h("div", { key: `${limit.limitId}-${window.windowDurationMins}`, className: "dshCodexQuotaWindow" },
513
+ h("div", { className: "dshCodexQuotaWindowHeader" },
514
+ h("span", null, label),
515
+ h("strong", null, `${t("quotaRemaining")} ${remainingText}%`)),
516
+ h("progress", {
517
+ max: 100,
518
+ value: remaining,
519
+ "aria-label": `${limitLabel} · ${label} · ${t("quotaRemaining")} ${remainingText}%`,
520
+ }),
521
+ h("div", { className: "dshCodexQuotaMeta" },
522
+ h("span", null, `${t("quotaUsed")} ${usedText}%`),
523
+ h("span", null, `${t("quotaResetsAt")}:${new Date(window.resetsAt).toLocaleString(t("locale"))}`)))
524
+ }))
525
+ }),
526
+ h("p", { className: "dshCodexMuted dshCodexQuotaUpdated" }, `${t("quotaUpdatedAt")}:${new Date(usage.observedAt).toLocaleString(t("locale"))}`))
527
+ }
528
+
196
529
  function objectAtPath(root, path) {
197
530
  let current = root
198
531
  for (const key of path) {
@@ -217,11 +550,28 @@ window.__ModuleLoader__.load({
217
550
  rows.push({
218
551
  id: candidate.id,
219
552
  ...(typeof candidate.name === "string" && candidate.name.length > 0 ? { name: candidate.name } : {}),
553
+ ...(Number.isSafeInteger(candidate.contextWindow) && candidate.contextWindow > 0
554
+ ? { contextWindow: candidate.contextWindow }
555
+ : {}),
556
+ ...(Number.isSafeInteger(candidate.maxTokens) && candidate.maxTokens > 0
557
+ ? { maxTokens: candidate.maxTokens }
558
+ : {}),
220
559
  })
221
560
  }
222
561
  return rows
223
562
  }
224
563
 
564
+ function formatModelTokenCount(value, locale) {
565
+ const divisor = value >= 1_000_000 ? 1_000_000 : value >= 1_000 ? 1_000 : 1
566
+ const suffix = divisor === 1_000_000 ? "M" : divisor === 1_000 ? "K" : ""
567
+ const compact = value / divisor
568
+ try {
569
+ return `${compact.toLocaleString(locale, { maximumFractionDigits: 1 })}${suffix}`
570
+ } catch {
571
+ return `${Math.round(compact * 10) / 10}${suffix}`
572
+ }
573
+ }
574
+
225
575
  function modelSavePlan(snapshot, selectedIds) {
226
576
  const selected = new Set(selectedIds)
227
577
  const modelIds = snapshot.models.map((model) => model.id)
@@ -340,6 +690,7 @@ window.__ModuleLoader__.load({
340
690
 
341
691
  function AuthorizationSettings({ client, t }) {
342
692
  const [snapshot, setSnapshot] = React.useState({ status: "loading" })
693
+ const [usageSnapshot, setUsageSnapshot] = React.useState({ status: "idle" })
343
694
  const [attempt, setAttempt] = React.useState(null)
344
695
  const [notices, setNotices] = React.useState([])
345
696
  const [prompt, setPrompt] = React.useState(null)
@@ -368,6 +719,29 @@ window.__ModuleLoader__.load({
368
719
  const value = await client.describe(controller.signal)
369
720
  if (!mountedRef.current || generation !== refreshGenerationRef.current || controller.signal.aborted) return
370
721
  setSnapshot({ status: "ready", value })
722
+ const credentialState = value?.credential?.state
723
+ const signedIn = credentialState === "signed-in"
724
+ || (credentialState === undefined && value?.credential?.configured === true)
725
+ if (!signedIn || typeof client.usage !== "function") {
726
+ setUsageSnapshot({ status: "idle" })
727
+ return
728
+ }
729
+ setUsageSnapshot((current) => ({
730
+ status: "loading",
731
+ ...(current.value === undefined ? {} : { value: current.value }),
732
+ }))
733
+ try {
734
+ const usage = safeAccountUsage(await client.usage(controller.signal))
735
+ if (usage === undefined) throw new Error("invalid account usage")
736
+ if (!mountedRef.current || generation !== refreshGenerationRef.current || controller.signal.aborted) return
737
+ setUsageSnapshot({ status: "ready", value: usage })
738
+ } catch {
739
+ if (!mountedRef.current || generation !== refreshGenerationRef.current || controller.signal.aborted) return
740
+ setUsageSnapshot((current) => ({
741
+ status: "error",
742
+ ...(current.value === undefined ? {} : { value: current.value }),
743
+ }))
744
+ }
371
745
  } catch {
372
746
  if (!mountedRef.current || generation !== refreshGenerationRef.current || controller.signal.aborted) return
373
747
  setSnapshot({ status: "error" })
@@ -381,9 +755,6 @@ window.__ModuleLoader__.load({
381
755
  React.useEffect(() => {
382
756
  mountedRef.current = true
383
757
  void refresh()
384
- const timer = typeof window.setInterval === "function"
385
- ? window.setInterval(() => void refresh(), 60_000)
386
- : undefined
387
758
  return () => {
388
759
  mountedRef.current = false
389
760
  refreshGenerationRef.current += 1
@@ -396,7 +767,6 @@ window.__ModuleLoader__.load({
396
767
  attemptRef.current = null
397
768
  current?.controller.abort()
398
769
  if (current !== null && !current.done) void client.cancel(current.id).catch(() => undefined)
399
- if (timer !== undefined) window.clearInterval(timer)
400
770
  }
401
771
  }, [client])
402
772
 
@@ -646,22 +1016,22 @@ window.__ModuleLoader__.load({
646
1016
  onClick: () => void logout(),
647
1017
  }, loggingOut ? t("signingOut") : t("signOut")) : null)
648
1018
 
1019
+ const liveUsage = usageSnapshot.value
649
1020
  let quotaBody
650
- if (quota.status === "recent-success") {
1021
+ if (liveUsage !== undefined) {
651
1022
  quotaBody = h(React.Fragment, null,
652
- h("p", { className: "dshCodexQuotaState" }, `${t("quotaRecentSuccess")} · ${new Date(quota.observedAt).toLocaleString()}`),
653
- h("p", { className: "dshCodexMuted" }, t("quotaSuccessCaution")))
654
- } else if (quota.status === "exhausted") {
1023
+ renderAccountUsage(liveUsage, t),
1024
+ usageSnapshot.status === "error"
1025
+ ? h("p", { role: "status", className: "dshCodexError" }, `${t("quotaLoadFailed")} ${t("quotaStaleHelp")}`)
1026
+ : null)
1027
+ } else if (usageSnapshot.status === "loading") {
1028
+ quotaBody = h("p", { className: "dshCodexMuted", "aria-busy": "true" }, t("quotaLoading"))
1029
+ } else if (usageSnapshot.status === "error") {
655
1030
  quotaBody = h(React.Fragment, null,
656
- h("p", { className: "dshCodexQuotaState dshCodexQuotaExhausted" }, t("quotaExhausted")),
657
- h("p", { className: "dshCodexMuted" }, `${t("quotaObservedAt")} · ${new Date(quota.observedAt).toLocaleString()}`),
658
- quota.resetAt === undefined
659
- ? h("p", { className: "dshCodexMuted" }, t("quotaNoReset"))
660
- : h("p", { className: "dshCodexMuted" }, `${t("quotaResetAt")} · ${new Date(quota.resetAt).toLocaleString()} · ${t("quotaResetIn")} ${quota.remainingMinutes} ${t("quotaMinutes")}`))
1031
+ h("p", { role: "status", className: "dshCodexError" }, t("quotaLoadFailed")),
1032
+ renderObservedQuota(quota, t))
661
1033
  } else {
662
- quotaBody = h(React.Fragment, null,
663
- h("p", { className: "dshCodexQuotaState dshCodexQuotaUnknown" }, t("quotaUnknown")),
664
- h("p", { className: "dshCodexMuted" }, t("quotaUnknownHelp")))
1034
+ quotaBody = renderObservedQuota(quota, t)
665
1035
  }
666
1036
 
667
1037
  return h("section", {
@@ -741,10 +1111,12 @@ window.__ModuleLoader__.load({
741
1111
  h("button", {
742
1112
  type: "button",
743
1113
  className: "dshCodexButton dshCodexRefreshButton",
744
- disabled: actionBusy,
1114
+ disabled: actionBusy || usageSnapshot.status === "loading",
745
1115
  onClick: refresh,
746
1116
  }, t("refresh"))),
747
- h("p", { className: "dshCodexQuotaProduct" }, t("quotaProduct")),
1117
+ liveUsage === undefined
1118
+ ? h("p", { className: "dshCodexQuotaProduct" }, t("quotaProduct"))
1119
+ : null,
748
1120
  quotaBody))
749
1121
  }
750
1122
 
@@ -851,20 +1223,40 @@ window.__ModuleLoader__.load({
851
1223
  const original = new Set(snapshot.selectedIds)
852
1224
  const dirty = selected.size !== original.size || [...selected].some((id) => !original.has(id))
853
1225
  const plan = modelSavePlan(snapshot, selectedIds)
854
- const modelItems = snapshot.models.map((model) => h("li", { key: model.id },
855
- h("label", {
856
- className: "dshCodexModelOption",
857
- "data-selected": selected.has(model.id) ? "true" : undefined,
858
- },
859
- h("input", {
860
- type: "checkbox",
861
- checked: selected.has(model.id),
862
- disabled: saving || !snapshot.writable,
863
- onChange: () => toggle(model.id),
864
- }),
865
- h("span", null,
866
- h("strong", null, model.name ?? model.id),
867
- model.name === undefined || model.name === model.id ? null : h("code", null, model.id)))))
1226
+ const modelItems = snapshot.models.map((model) => {
1227
+ const metadata = [
1228
+ ...(model.contextWindow === undefined
1229
+ ? []
1230
+ : [`${t("modelsContextWindow")} ${formatModelTokenCount(model.contextWindow, t("locale"))}`]),
1231
+ ...(model.maxTokens === undefined
1232
+ ? []
1233
+ : [`${t("modelsMaxOutput")} ${formatModelTokenCount(model.maxTokens, t("locale"))}`]),
1234
+ ]
1235
+ return h("li", { key: model.id },
1236
+ h("label", {
1237
+ className: "dshCodexModelOption",
1238
+ "data-selected": selected.has(model.id) ? "true" : undefined,
1239
+ },
1240
+ h("input", {
1241
+ type: "checkbox",
1242
+ checked: selected.has(model.id),
1243
+ disabled: saving || !snapshot.writable,
1244
+ onChange: () => toggle(model.id),
1245
+ }),
1246
+ h("span", { className: "dshCodexModelCopy" },
1247
+ h("span", { className: "dshCodexModelHeading" },
1248
+ h("strong", null, model.name ?? model.id),
1249
+ model.name === undefined || model.name === model.id
1250
+ ? null
1251
+ : h("code", { className: "dshCodexModelId" }, model.id)),
1252
+ metadata.length === 0
1253
+ ? null
1254
+ : h("span", { className: "dshCodexModelCapabilities" },
1255
+ ...metadata.map((label) => h("span", {
1256
+ key: label,
1257
+ className: "dshCodexModelBadge",
1258
+ }, label))))))
1259
+ })
868
1260
  content = h(React.Fragment, null,
869
1261
  snapshot.writable ? null : h("p", { className: "dshCodexMuted" }, t("modelsReadOnly")),
870
1262
  h("ul", {
@@ -915,6 +1307,7 @@ window.__ModuleLoader__.load({
915
1307
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-codex: client dictionaries")
916
1308
  const t = ctx.locale.bind(NS)
917
1309
  const client = createAuthorizationClient(ctx.connection)
1310
+ const sessionPreferenceClient = createSessionPreferenceClient(ctx.connection)
918
1311
  const modelClient = createModelEnablementClient(ctx.connection, ctx.settingsScope?.describe?.())
919
1312
  ctx.slots.inject("settings.section", () => ctx.slots.register({
920
1313
  name: "settings.section",
@@ -924,6 +1317,25 @@ window.__ModuleLoader__.load({
924
1317
  locale: NS,
925
1318
  inject: () => ({ client, modelClient, t }),
926
1319
  }, CodexSettings))
1320
+ ctx.inject?.(["slots", "modelDirectories"], (scope) => {
1321
+ scope.slots.inject("conversation.input.right", () => scope.slots.register({
1322
+ name: "conversation.input.right",
1323
+ id: "dsh-codex-fast",
1324
+ order: 100,
1325
+ locale: NS,
1326
+ inject: (sessionId) => {
1327
+ const directory = scope.modelDirectories.directoryFor(sessionId)
1328
+ return {
1329
+ preferenceClient: sessionPreferenceClient.forSession(sessionId),
1330
+ selectModel: (selection) => directory.select(selection),
1331
+ t,
1332
+ hooks: {
1333
+ modelDirectory: directory.store,
1334
+ },
1335
+ }
1336
+ },
1337
+ }, CodexFastToggle))
1338
+ })
927
1339
  }
928
1340
 
929
1341
  function installStyle() {
@@ -936,16 +1348,15 @@ window.__ModuleLoader__.load({
936
1348
  document.head.appendChild(style)
937
1349
  }
938
1350
  style.textContent = [
939
- "[role=dialog]:has(.dshCodexPage){width:min(1180px,calc(100vw - 48px))}",
940
- ".dshCodexPage{box-sizing:border-box;display:flex;width:100%;max-width:1120px;min-width:0;flex-direction:column;gap:28px;padding:6px 0 40px;color:var(--dsw-alias-label-primary)}",
1351
+ ".dshCodexPage{box-sizing:border-box;display:flex;width:100%;min-width:0;flex-direction:column;gap:14px;padding:6px 0 32px;color:var(--dsw-alias-label-primary);font:inherit}",
941
1352
  ".dshCodexPage *{box-sizing:border-box}",
942
1353
  ".dshCodexHero{display:flex;min-width:0;flex-direction:column;gap:8px}",
943
1354
  ".dshCodexHero h2,.dshCodexHero p{margin:0}",
944
1355
  ".dshCodexHero h2{font-size:16px;font-weight:500;line-height:24px}",
945
1356
  ".dshCodexHero p{max-width:760px;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}",
946
- ".dshCodexPanel{display:flex;width:100%;min-width:0;max-width:none;color:var(--dsw-alias-label-primary);flex-direction:column;gap:18px}",
1357
+ ".dshCodexPanel{display:flex;width:100%;min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px}",
947
1358
  ".dshCodexPanel p,.dshCodexPanel label{overflow-wrap:anywhere;word-break:break-word}",
948
- ".dshCodexCard{border:1px solid var(--dsw-alias-border-l2);border-radius:18px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-3));padding:clamp(24px,3vw,34px)}",
1359
+ ".dshCodexCard{border:1px solid var(--dsw-alias-border-l2);border-radius:18px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-3));padding:20px}",
949
1360
  ".dshCodexHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}",
950
1361
  ".dshCodexHeader>div{min-width:0}",
951
1362
  ".dshCodexHeader h3,.dshCodexHeader p,.dshCodexNotices p,.dshCodexQuota p{margin:0}",
@@ -957,7 +1368,7 @@ window.__ModuleLoader__.load({
957
1368
  ".dshCodexStatus[data-state=active] .dshCodexStatusDot{background:var(--dsw-alias-state-success-primary)}",
958
1369
  ".dshCodexStatus[data-state=invalid] .dshCodexStatusDot{background:var(--dsw-alias-state-error-primary)}",
959
1370
  ".dshCodexStatus[data-state=busy] .dshCodexStatusDot{background:var(--dsw-alias-state-business-primary)}",
960
- ".dshCodexQuota{display:flex;min-width:0;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:24px}",
1371
+ ".dshCodexQuota{display:flex;min-width:0;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:18px}",
961
1372
  ".dshCodexQuotaHeader{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}",
962
1373
  ".dshCodexQuota h3{margin:0;font-size:16px;font-weight:500;line-height:24px}",
963
1374
  ".dshCodexQuotaProduct{font-size:14px;font-weight:500;line-height:22px}",
@@ -966,6 +1377,19 @@ window.__ModuleLoader__.load({
966
1377
  ".dshCodexQuotaUnknown::before{background:var(--dsw-alias-label-tertiary)}",
967
1378
  ".dshCodexQuotaExhausted::before{background:var(--dsw-alias-state-error-primary)}",
968
1379
  ".dshCodexQuotaExhausted{color:var(--dsw-alias-state-error-primary)}",
1380
+ ".dshCodexQuotaGroup{display:flex;min-width:0;flex-direction:column;gap:10px}",
1381
+ ".dshCodexQuotaGroup+.dshCodexQuotaGroup{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}",
1382
+ ".dshCodexQuotaWindow{display:flex;min-width:0;flex-direction:column;gap:7px}",
1383
+ ".dshCodexQuotaWindow+.dshCodexQuotaWindow{margin-top:4px}",
1384
+ ".dshCodexQuotaWindowHeader,.dshCodexQuotaMeta{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}",
1385
+ ".dshCodexQuotaWindowHeader{font-size:14px;line-height:22px}",
1386
+ ".dshCodexQuotaWindowHeader strong{font-weight:400;color:var(--dsw-alias-label-secondary)}",
1387
+ ".dshCodexQuotaMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
1388
+ ".dshCodexQuotaWindow progress{display:block;width:100%;height:8px;overflow:hidden;border:0;border-radius:999px;appearance:none;-webkit-appearance:none;background:var(--dsw-alias-bg-layer-1)}",
1389
+ ".dshCodexQuotaWindow progress::-webkit-progress-bar{border-radius:999px;background:var(--dsw-alias-bg-layer-1)}",
1390
+ ".dshCodexQuotaWindow progress::-webkit-progress-value{border-radius:999px;background:var(--dsw-alias-state-business-primary)}",
1391
+ ".dshCodexQuotaWindow progress::-moz-progress-bar{border-radius:999px;background:var(--dsw-alias-state-business-primary)}",
1392
+ ".dshCodexQuotaUpdated{margin-top:2px!important}",
969
1393
  ".dshCodexMuted{color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}",
970
1394
  ".dshCodexWarning{max-width:820px;color:var(--dsw-alias-label-secondary);font-size:14px;line-height:22px}",
971
1395
  ".dshCodexActions{display:flex;min-width:0;flex-wrap:wrap;gap:8px}",
@@ -976,6 +1400,15 @@ window.__ModuleLoader__.load({
976
1400
  ".dshCodexButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}",
977
1401
  ".dshCodexButton:focus-visible,.dshCodexModelOption:has(input:focus-visible){outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-border-l3));outline-offset:2px}",
978
1402
  ".dshCodexButton:disabled{cursor:not-allowed;opacity:.5}",
1403
+ ".dshCodexFastToggle{box-sizing:border-box;display:inline-flex;width:28px;height:28px;flex:none;align-items:center;justify-content:center;border:0;border-radius:999px;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;padding:0;transition:background-color .15s ease,color .15s ease}",
1404
+ ".dshCodexFastToggle:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-primary)}",
1405
+ ".dshCodexFastToggle[data-fast=true]{background:var(--dsw-alias-state-business-secondary,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-state-business-primary)}",
1406
+ ".dshCodexFastToggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-border-l3));outline-offset:2px}",
1407
+ ".dshCodexFastToggle:disabled{cursor:not-allowed;opacity:.45}",
1408
+ ".dshCodexLegacyRepair{box-sizing:border-box;display:inline-flex;min-width:0;height:28px;flex:none;align-items:center;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-secondary));font:inherit;font-size:12px;line-height:18px;cursor:pointer;padding:0 10px;white-space:nowrap}",
1409
+ ".dshCodexLegacyRepair:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}",
1410
+ ".dshCodexLegacyRepair:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-border-l3));outline-offset:2px}",
1411
+ ".dshCodexLegacyRepair:disabled{cursor:not-allowed;opacity:.55}",
979
1412
  ".dshCodexPrimary{border-color:var(--dsw-alias-button-primary-fill,var(--dsw-alias-state-business-primary));background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-state-business-primary));color:var(--dsw-alias-label-primary-foreground,#fff)}",
980
1413
  ".dshCodexPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-state-business-primary))}",
981
1414
  ".dshCodexModelList{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:36px;row-gap:8px;margin:0;padding:0;list-style:none}",
@@ -984,9 +1417,12 @@ window.__ModuleLoader__.load({
984
1417
  ".dshCodexModelOption:has(input:disabled){cursor:not-allowed;opacity:.65}",
985
1418
  ".dshCodexModelOption:has(input:disabled):hover{background:transparent}",
986
1419
  ".dshCodexModelOption input{width:18px;height:18px;flex:none;margin:2px 0 0;accent-color:var(--dsw-alias-state-business-primary)}",
987
- ".dshCodexModelOption span{display:flex;min-width:0;flex-wrap:wrap;align-items:baseline;column-gap:8px;row-gap:2px}",
1420
+ ".dshCodexModelCopy{display:flex;min-width:0;flex:1;flex-direction:column;gap:7px}",
1421
+ ".dshCodexModelHeading{display:flex;min-width:0;flex-direction:column;gap:1px}",
988
1422
  ".dshCodexModelOption strong{overflow-wrap:anywhere;font-size:14px;font-weight:500;line-height:22px}",
989
- ".dshCodexModelOption code{overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary);font-family:inherit;font-size:12px;line-height:18px}",
1423
+ ".dshCodexModelId{overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary);font-family:inherit;font-size:12px;line-height:18px}",
1424
+ ".dshCodexModelCapabilities{display:flex;min-width:0;flex-wrap:wrap;gap:6px}",
1425
+ ".dshCodexModelBadge{display:inline-flex;align-items:center;border-radius:999px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:18px;padding:2px 8px}",
990
1426
  ".dshCodexModelActions{justify-content:flex-end;border-top:1px solid var(--dsw-alias-border-l2);padding-top:20px}",
991
1427
  ".dshCodexNotices{display:flex;min-width:0;flex-direction:column;gap:10px;margin:0;padding:0;list-style:none}",
992
1428
  ".dshCodexNotices li,.dshCodexPrompt{box-sizing:border-box;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:14px 16px}",
@@ -998,9 +1434,9 @@ window.__ModuleLoader__.load({
998
1434
  ".dshCodexPrompt input,.dshCodexPrompt select{box-sizing:border-box;width:100%;min-width:0;height:38px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;padding:0 10px}",
999
1435
  ".dshCodexError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}",
1000
1436
  ".dshCodexSuccess{margin:0;color:var(--dsw-alias-state-success-primary);font-size:12px;line-height:18px}",
1001
- "@media(max-width:760px){.dshCodexCard{padding:24px}.dshCodexModelList{grid-template-columns:1fr}.dshCodexModelActions{justify-content:flex-start}}",
1437
+ "@media(max-width:760px){.dshCodexModelList{grid-template-columns:1fr}.dshCodexModelActions{justify-content:flex-start}}",
1002
1438
  "@media(max-width:640px){.dshCodexAuthToolbar,.dshCodexHeader{align-items:stretch;flex-direction:column}.dshCodexAuthActions{margin-left:0;justify-content:flex-start}}",
1003
- "@media(max-width:480px){[role=dialog]:has(.dshCodexPage){flex-direction:column}[role=dialog]:has(.dshCodexPage)>nav{box-sizing:border-box;width:100%;height:auto;flex:none;gap:0;overflow:hidden;padding:10px 12px 8px}[role=dialog]:has(.dshCodexPage)>nav>div:first-child{display:none}[role=dialog]:has(.dshCodexPage)>nav>div:last-child{display:flex;width:100%;flex-direction:row;gap:4px;overflow-x:auto;padding-bottom:2px}[role=dialog]:has(.dshCodexPage)>nav>div:last-child>*{flex:none}[role=dialog]:has(.dshCodexPage)>:not(nav){width:100%;min-width:0;min-height:0;overflow:hidden;flex:1 1 0%}.dshCodexPage{gap:20px;padding:0 0 28px}.dshCodexCard{border-radius:14px;padding:18px}.dshCodexActions{align-items:stretch;flex-direction:column}.dshCodexButton{width:100%}}",
1439
+ "@media(max-width:480px){.dshCodexPage{gap:12px;padding:0 0 24px}.dshCodexCard{border-radius:14px;padding:16px}.dshCodexQuotaWindowHeader,.dshCodexQuotaMeta{align-items:flex-start;flex-direction:column;gap:2px}.dshCodexActions{align-items:stretch;flex-direction:column}.dshCodexButton{width:100%}}",
1004
1440
  ].join("")
1005
1441
  const references = style[STYLE_REF_KEY]
1006
1442
  style[STYLE_REF_KEY] = Number.isSafeInteger(references) && references > 0
@@ -1024,12 +1460,14 @@ window.__ModuleLoader__.load({
1024
1460
  exports.AuthorizationSettings = AuthorizationSettings
1025
1461
  exports.CHANNEL = CHANNEL
1026
1462
  exports.CODEX_PROVIDER = CODEX_PROVIDER
1463
+ exports.CodexFastToggle = CodexFastToggle
1027
1464
  exports.CodexSettings = CodexSettings
1028
1465
  exports.ModelEnablementSettings = ModelEnablementSettings
1029
1466
  exports.NS = NS
1030
1467
  exports.apply = apply
1031
1468
  exports.createAuthorizationClient = createAuthorizationClient
1032
1469
  exports.createModelEnablementClient = createModelEnablementClient
1470
+ exports.createSessionPreferenceClient = createSessionPreferenceClient
1033
1471
  exports.inject = inject
1034
1472
  exports.safeQuotaSnapshot = safeQuotaSnapshot
1035
1473
  return module.exports