@cortexkit/opencode-openai-auth 1.2.0 → 1.4.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 (90) hide show
  1. package/dist/bin.js +8 -8
  2. package/dist/{codex-request-CjXETpTA.d.ts → codex-request-hw0XcldE.d.ts} +146 -29
  3. package/dist/index-1eq2s1cp.js +2 -0
  4. package/dist/{index-qp5hdpw8.js → index-1tdnrt68.js} +1 -1
  5. package/dist/index-5230x6s8.js +2 -0
  6. package/dist/{index-qxbchh2d.js → index-ag04nyv7.js} +7 -7
  7. package/dist/index-brbqqer5.js +90 -0
  8. package/dist/{index-xwvze96g.js → index-d98f1xdp.js} +1 -1
  9. package/dist/index-jcyrm58f.js +65 -0
  10. package/dist/{index-htvcmtcx.js → index-rsvnabbe.js} +2 -2
  11. package/dist/{index-ftkhhr6d.js → index-s88n2bjn.js} +1 -1
  12. package/dist/{index-bc89akak.js → index-t5m0dhmq.js} +1 -1
  13. package/dist/index-vy729a0b.js +69 -0
  14. package/dist/index-wgwze391.js +2 -0
  15. package/dist/{index-ffg0sgg5.js → index-yhw3hnsh.js} +2 -2
  16. package/dist/index.d.ts +11 -3
  17. package/dist/index.js +1 -1
  18. package/dist/{model-CQcw10oW.d.ts → model-DphbMQce.d.ts} +1 -1
  19. package/dist/{port-file-BDJnzgHH.d.ts → port-file-Cx-wwe5Q.d.ts} +1 -1
  20. package/dist/{protocol-C-kagE6O.d.ts → protocol-CDqdjh74.d.ts} +1 -1
  21. package/dist/rpc/port-file.d.ts +1 -1
  22. package/dist/rpc/port-file.js +1 -1
  23. package/dist/rpc/protocol.d.ts +1 -1
  24. package/dist/rpc/protocol.js +1 -1
  25. package/dist/rpc/rpc-client.d.ts +3 -3
  26. package/dist/rpc/rpc-client.js +1 -1
  27. package/dist/rpc/rpc-dir.js +1 -1
  28. package/dist/{sidebar-state-BfQ4mpZO.d.ts → sidebar-state-pTd9gg9W.d.ts} +71 -7
  29. package/dist/sidebar-state.d.ts +2 -2
  30. package/dist/sidebar-state.js +1 -1
  31. package/dist/{token-z6qq7a1d.js → token-0z5g59p8.js} +1 -1
  32. package/dist/token-util-jf9g6stk.js +1 -0
  33. package/dist/tui-preferences.js +1 -1
  34. package/dist/tui.d.ts +30 -3
  35. package/dist/v2/server.d.ts +2 -2
  36. package/dist/v2/server.js +2 -2
  37. package/package.json +6 -6
  38. package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +119 -6
  39. package/src/tui-compiled/raw/shared/0f77ebc3c914c9c6-custody.js +294 -16
  40. package/src/tui-compiled/raw/shared/169b2b4bf30909bc-consumer.js +178 -26
  41. package/src/tui-compiled/raw/shared/2577ec69f9973ae9-quota-normalize.js +45 -1
  42. package/src/tui-compiled/raw/shared/30f5bc480f31f51c-roster.js +100 -43
  43. package/src/tui-compiled/raw/shared/375d9cca2def037d-private-files.js +10 -2
  44. package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +3 -1
  45. package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +7 -9
  46. package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +2 -0
  47. package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +17 -3
  48. package/src/tui-compiled/raw/shared/81dddd203f1b92a3-quota-writer.js +253 -0
  49. package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +31 -6
  50. package/src/tui-compiled/raw/shared/94f53cfd572ba4bb-availability.js +106 -0
  51. package/src/tui-compiled/raw/shared/abd062d633e29fd2-pool-routing.js +116 -12
  52. package/src/tui-compiled/raw/shared/bfbdd0f9aae56b47-pool-quota.js +15 -3
  53. package/src/tui-compiled/raw/shared/d0f0b6be200311af-insula.js +348 -21
  54. package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +2 -2
  55. package/src/tui-compiled/raw/shared/dbc61cd405941828-accounts.js +5 -4
  56. package/src/tui-compiled/raw/shared/dc9290c2a7552ba6-watch.js +254 -0
  57. package/src/tui-compiled/raw/shared/e8d3c8da776ef9cd-index.js +5 -1
  58. package/src/tui-compiled/raw/sidebar-state.js +32 -3
  59. package/src/tui-compiled/raw/tui/v2-dialog.js +2 -1
  60. package/src/tui-compiled/raw/tui.tsx +170 -26
  61. package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +119 -6
  62. package/src/tui-compiled/runtime/shared/0f77ebc3c914c9c6-custody.js +294 -16
  63. package/src/tui-compiled/runtime/shared/169b2b4bf30909bc-consumer.js +178 -26
  64. package/src/tui-compiled/runtime/shared/2577ec69f9973ae9-quota-normalize.js +45 -1
  65. package/src/tui-compiled/runtime/shared/30f5bc480f31f51c-roster.js +100 -43
  66. package/src/tui-compiled/runtime/shared/375d9cca2def037d-private-files.js +10 -2
  67. package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +3 -1
  68. package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +7 -9
  69. package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +2 -0
  70. package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +17 -3
  71. package/src/tui-compiled/runtime/shared/81dddd203f1b92a3-quota-writer.js +253 -0
  72. package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +31 -6
  73. package/src/tui-compiled/runtime/shared/94f53cfd572ba4bb-availability.js +106 -0
  74. package/src/tui-compiled/runtime/shared/abd062d633e29fd2-pool-routing.js +116 -12
  75. package/src/tui-compiled/runtime/shared/bfbdd0f9aae56b47-pool-quota.js +15 -3
  76. package/src/tui-compiled/runtime/shared/d0f0b6be200311af-insula.js +348 -21
  77. package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +2 -2
  78. package/src/tui-compiled/runtime/shared/dbc61cd405941828-accounts.js +5 -4
  79. package/src/tui-compiled/runtime/shared/dc9290c2a7552ba6-watch.js +254 -0
  80. package/src/tui-compiled/runtime/shared/e8d3c8da776ef9cd-index.js +5 -1
  81. package/src/tui-compiled/runtime/sidebar-state.js +32 -3
  82. package/src/tui-compiled/runtime/tui/v2-dialog.js +2 -1
  83. package/src/tui-compiled/runtime/tui.js +350 -219
  84. package/dist/index-03em8hv1.js +0 -69
  85. package/dist/index-6n4pnfkg.js +0 -2
  86. package/dist/index-ehjj89rx.js +0 -2
  87. package/dist/index-f21addzm.js +0 -90
  88. package/dist/index-r2s51ac1.js +0 -2
  89. package/dist/index-rwbrhkse.js +0 -65
  90. package/dist/token-util-cf0nyh7n.js +0 -1
@@ -0,0 +1,254 @@
1
+ /**
2
+ * While a vault watch is healthy, the roster is still read this often: the
3
+ * watch carries no history, so a lost notice or a silently dead stream is
4
+ * caught within this period.
5
+ */
6
+ export const SAFETY_POLL_MS = 5 * 60_000;
7
+ /**
8
+ * After a watch fails without being accepted, the host polls and tries one
9
+ * new watch this much later, so a refusing or draining module is never asked
10
+ * in a tight loop.
11
+ */
12
+ export const WATCH_RETRY_MS = 5 * 60_000;
13
+ /**
14
+ * First wait before re-subscribing a watch that closed after it had been
15
+ * accepted. It doubles for each further close in a row, up to WATCH_RETRY_MS.
16
+ */
17
+ export const RESUBSCRIBE_BACKOFF_MS = 1_000;
18
+ /**
19
+ * Tells whether a notice follows the previous one with nothing lost between
20
+ * them. `seq` restarts at 1 for every watch and `epoch` names the sending
21
+ * module's process lifetime. A sequence gap (notices are sheddable), a
22
+ * different epoch (the module restarted) or a notice of an unknown kind all
23
+ * mean changes may have been missed, so the caller must re-read everything.
24
+ */
25
+ export class ChangeNoticeReader {
26
+ #epoch;
27
+ #seq = 0;
28
+ /**
29
+ * Begin a new watch, whose first notice is seq 1 again. The epoch is kept,
30
+ * so a module restart between two watches still reads as a break.
31
+ */
32
+ restart() {
33
+ this.#seq = 0;
34
+ }
35
+ continuous(notice) {
36
+ const continuous = notice.kind === 'changed' &&
37
+ notice.seq === this.#seq + 1 &&
38
+ (this.#epoch === undefined || notice.epoch === this.#epoch);
39
+ this.#epoch = notice.epoch;
40
+ this.#seq = notice.seq;
41
+ return continuous;
42
+ }
43
+ }
44
+ /**
45
+ * Runs `read` with at most one call in flight and at most one more queued
46
+ * behind it, however many requests arrive meanwhile. A request made while a
47
+ * read is running may have arrived after that read took its snapshot, so it
48
+ * earns exactly one more read; any further requests share that one.
49
+ */
50
+ export class CoalescedRead {
51
+ #read;
52
+ #onError;
53
+ #running;
54
+ #queued = false;
55
+ constructor(read, onError) {
56
+ this.#read = read;
57
+ this.#onError = onError;
58
+ }
59
+ /** The read in flight (including its queued follow-up), if any. */
60
+ get running() {
61
+ return this.#running;
62
+ }
63
+ request() {
64
+ if (this.#running) {
65
+ this.#queued = true;
66
+ return this.#running;
67
+ }
68
+ this.#running = (async () => {
69
+ do {
70
+ this.#queued = false;
71
+ try {
72
+ await this.#read();
73
+ }
74
+ catch (error) {
75
+ this.#onError(error);
76
+ }
77
+ } while (this.#queued);
78
+ })().finally(() => {
79
+ this.#running = undefined;
80
+ });
81
+ return this.#running;
82
+ }
83
+ }
84
+ /**
85
+ * Keeps one vault `watchScoped` open for a host and runs the host's discovery
86
+ * when a notice shows the visible account list changed. Discovery runs only
87
+ * when the notice's view differs from the last discovered roster's view, when
88
+ * a notice shows a gap or a new epoch, or on the first notice of a watch that
89
+ * was re-subscribed after a close (the time between two watches was not
90
+ * observed). It never opens a second watch while one is open.
91
+ */
92
+ export class ScopedRosterWatch {
93
+ #options;
94
+ #reader = new ChangeNoticeReader();
95
+ #reads;
96
+ #mode = 'polling';
97
+ #watch;
98
+ #timer;
99
+ #stopped = false;
100
+ #latestView;
101
+ #force = false;
102
+ /**
103
+ * How many accepted watches in a row have closed; each one doubles the wait
104
+ * before the next re-subscribe. Reset once a watch stays up five minutes.
105
+ */
106
+ #closes = 0;
107
+ constructor(options) {
108
+ this.#options = options;
109
+ this.#reads = new CoalescedRead(async () => {
110
+ const force = this.#force;
111
+ this.#force = false;
112
+ // A read is only ever requested by a notice, which set #latestView.
113
+ if (force || this.#latestView !== this.#options.view())
114
+ await this.#options.discover(this.#latestView ?? '');
115
+ }, (error) => {
116
+ if (!this.#stopped)
117
+ this.#options.onError?.(error);
118
+ });
119
+ }
120
+ get mode() {
121
+ return this.#mode;
122
+ }
123
+ start() {
124
+ if (this.#stopped || this.#watch || this.#timer)
125
+ return;
126
+ if (typeof this.#options.client.watchScoped !== 'function') {
127
+ // A claustrum-client older than 0.12.0: poll exactly as before.
128
+ this.#setMode('fallback', 'client-without-watch');
129
+ return;
130
+ }
131
+ void this.#open(false);
132
+ }
133
+ stop() {
134
+ if (this.#stopped)
135
+ return;
136
+ this.#stopped = true;
137
+ if (this.#timer)
138
+ (this.#options.clearTimeoutImpl ?? clearTimeout)(this.#timer);
139
+ this.#timer = undefined;
140
+ const watch = this.#watch;
141
+ this.#watch = undefined;
142
+ watch?.handle.stop();
143
+ }
144
+ #setMode(mode, reason) {
145
+ if (this.#mode === mode)
146
+ return;
147
+ this.#mode = mode;
148
+ this.#options.logger?.debug('claustrum vault watch', { mode, reason });
149
+ this.#options.onMode?.(mode);
150
+ }
151
+ #later(delay, run) {
152
+ this.#timer = (this.#options.setTimeoutImpl ?? setTimeout)(() => {
153
+ this.#timer = undefined;
154
+ if (!this.#stopped)
155
+ run();
156
+ }, delay);
157
+ this.#timer.unref?.();
158
+ }
159
+ async #open(resubscribed) {
160
+ let token;
161
+ try {
162
+ token = await this.#options.enrollmentToken();
163
+ }
164
+ catch (error) {
165
+ if (this.#stopped)
166
+ return;
167
+ this.#options.onError?.(error);
168
+ this.#fallback('token-unreadable');
169
+ return;
170
+ }
171
+ const watchScoped = this.#options.client.watchScoped;
172
+ if (this.#stopped || this.#watch || !watchScoped)
173
+ return;
174
+ this.#reader.restart();
175
+ // A re-subscribed watch reads on its first notice: changes made while no
176
+ // watch was open were never announced.
177
+ this.#force = resubscribed;
178
+ let watch;
179
+ let handle;
180
+ try {
181
+ handle = watchScoped.call(this.#options.client, (notice) => {
182
+ if (this.#stopped || !watch || this.#watch !== watch)
183
+ return;
184
+ watch.notices++;
185
+ if (watch.notices === 1) {
186
+ this.#options.onReachable?.();
187
+ watch.acceptedAt = (this.#options.now ?? Date.now)();
188
+ this.#setMode('watching', 'accepted');
189
+ }
190
+ if (!this.#reader.continuous(notice))
191
+ this.#force = true;
192
+ this.#latestView = notice.view;
193
+ // The read itself decides whether to list, against the roster as
194
+ // it stands when the read runs.
195
+ void this.#reads.request();
196
+ }, { enrollmentToken: token });
197
+ }
198
+ catch (error) {
199
+ this.#options.onError?.(error);
200
+ this.#fallback('watch-failed');
201
+ return;
202
+ }
203
+ watch = { handle, notices: 0 };
204
+ this.#watch = watch;
205
+ this.#setMode('polling', resubscribed ? 'resubscribing' : 'opening');
206
+ const opened = watch;
207
+ handle.closed.then((end) => this.#closed(opened, end), (error) => this.#closed(opened, { kind: 'transport_lost', error }));
208
+ }
209
+ #closed(watch, end) {
210
+ if (this.#stopped || this.#watch !== watch)
211
+ return;
212
+ this.#watch = undefined;
213
+ if (end.kind === 'stopped')
214
+ return;
215
+ if (end.kind === 'transport_lost')
216
+ this.#options.onTransportLost?.();
217
+ if (end.kind === 'unsupported') {
218
+ // An older vault answers unknown_method: poll exactly as before.
219
+ this.#setMode('fallback', 'vault-without-watch');
220
+ return;
221
+ }
222
+ if (end.kind === 'refused') {
223
+ this.#fallback('refused');
224
+ try {
225
+ this.#options.onRefused(end.error);
226
+ }
227
+ catch (error) {
228
+ this.#options.onError?.(error);
229
+ }
230
+ return;
231
+ }
232
+ if (watch.notices > 0) {
233
+ // An accepted watch that closed (the vault restarted, or a revocation
234
+ // ended it) gets one new watch. A revocation then shows up as that
235
+ // watch's refusal.
236
+ const now = (this.#options.now ?? Date.now)();
237
+ if (watch.acceptedAt !== undefined &&
238
+ now - watch.acceptedAt >= SAFETY_POLL_MS)
239
+ this.#closes = 0;
240
+ const delay = Math.min(RESUBSCRIBE_BACKOFF_MS * 2 ** Math.min(this.#closes, 16), WATCH_RETRY_MS);
241
+ this.#closes++;
242
+ this.#setMode('polling', 'resubscribing');
243
+ this.#later(delay, () => void this.#open(true));
244
+ return;
245
+ }
246
+ // Zero notices: the watch was never accepted (the vault is draining, or
247
+ // the connection failed). The client already made one read to learn why.
248
+ this.#fallback(end.kind);
249
+ }
250
+ #fallback(reason) {
251
+ this.#setMode('fallback', reason);
252
+ this.#later(WATCH_RETRY_MS, () => void this.#open(false));
253
+ }
254
+ }
@@ -6,13 +6,17 @@
6
6
  import { ClaustrumClient, } from "@cortexkit/claustrum-client";
7
7
  import { insulaUsageTransport } from "./d0f0b6be200311af-insula.js";
8
8
  export { ClaustrumCredentialError, } from "@cortexkit/claustrum-client";
9
+ export { ClaustrumUnreachableError, classifyVaultFailure, VAULT_FAILURE_CLASSES, } from "./94f53cfd572ba4bb-availability.js";
9
10
  export { ClaustrumConsumer, } from "./169b2b4bf30909bc-consumer.js";
10
- export { ClaustrumScopedCustody, decideScopedRetryAfter401, isScopedCredentialRotation, SERVING_MARGIN_MS, } from "./0f77ebc3c914c9c6-custody.js";
11
+ export { ClaustrumScopedCustody, decideScopedRetryAfter401, isScopedCredentialRotation, KEPT_RECEIPT_MARGIN_MS, SERVING_MARGIN_MS, } from "./0f77ebc3c914c9c6-custody.js";
11
12
  export { ClaustrumEnrollmentManager, classifyEnrollmentError, connectClaustrumEnrollmentClient, disconnectClaustrumEnrollment, enrollmentAuthority, enrollmentName, getClaustrumEnrollmentPaths, hostEnrollmentPaths, RETRYABLE_ENROLLMENT_CODES, readClaustrumEnrollmentStatus, readClaustrumEnrollmentToken, resetClaustrumEnrollmentState, TERMINAL_ENROLLMENT_CODES, } from "./b9588ad4725c1d10-enrollment.js";
12
13
  export { ClaustrumConsumerError, } from "./ca0fccaba1d87af4-errors.js";
13
14
  export { assertHostSlotMatchesMode, assertNotCustodyPlaceholder, CUSTODY_PLACEHOLDER_PREFIX, classifyHostSlot, custodyPlaceholder, custodyPlaceholderKey, isCustodyPlaceholder, isCustodyPlaceholderValue, } from "./d9d2892a4c371301-host-slot.js";
15
+ export { acquireInsulaQuotaSource, } from "./d0f0b6be200311af-insula.js";
14
16
  export { acceptAccount, declineAccount, isDeclined, } from "./7c140680fc81be92-interlock.js";
17
+ export { drainVaultQuota } from "./81dddd203f1b92a3-quota-writer.js";
15
18
  export { acceptVaultRoute, DEFAULT_ROUTE_PREFIX, declineVaultRoute, mutateVaultRoster, projectVaultRoster, readVaultRoster, recordVaultAccountQuota, recordVaultQuota, refreshVaultRoster, resolveVaultPrimary, vaultRoutingRows, } from "./30f5bc480f31f51c-roster.js";
19
+ export { ChangeNoticeReader, RESUBSCRIBE_BACKOFF_MS, SAFETY_POLL_MS, ScopedRosterWatch, WATCH_RETRY_MS, } from "./dc9290c2a7552ba6-watch.js";
16
20
  /**
17
21
  * Connect the client that lists and fetches this consumer's vault credentials
18
22
  * on the request path. `connectionFile` is required: this library
@@ -2,13 +2,35 @@ import {
2
2
  formatQuota,
3
3
  isQuotaStale,
4
4
  projectQuota,
5
+ quotaCreditsText,
5
6
  quotaWindowName
6
7
  } from "./shared/d5a8f1061ee2f114-index.js";
7
8
  import { orderForPlacement } from "./shared/3e7d45a77c6ad4d5-index.js";
8
9
  import {
10
+ creditsUsable,
9
11
  orderedPlacement,
10
12
  vaultMainRouteId
11
13
  } from "./shared/abd062d633e29fd2-pool-routing.js";
14
+ export function accountCreditsText(quota) {
15
+ const credits = quota?.credits;
16
+ if (credits === null || typeof credits !== "object")
17
+ return;
18
+ const { hasCredits, unlimited, balance } = credits;
19
+ if (typeof hasCredits !== "boolean" || typeof unlimited !== "boolean")
20
+ return;
21
+ return quotaCreditsText({
22
+ hasCredits,
23
+ unlimited,
24
+ ...typeof balance === "number" ? { balance } : {}
25
+ });
26
+ }
27
+ export function accountServesOnCredits(quota, now = Date.now()) {
28
+ if (!exhaustedQuotaResetAt(quota, now))
29
+ return !1;
30
+ if (spendControlExhaustedResetAt(quota, now))
31
+ return !1;
32
+ return creditsUsable(quota?.credits, now);
33
+ }
12
34
  const QUOTA_WINDOW_KEYS = ["primary", "secondary"], LEGACY_WINDOW_MINUTES = {
13
35
  primary: 300,
14
36
  secondary: 10080
@@ -153,7 +175,7 @@ export function normalizeSidebarState(raw) {
153
175
  };
154
176
  } else
155
177
  main = { quota: null, killed: !1 };
156
- const fallbacks = normalizeAccountList(r.fallbacks), vaultAccounts = Array.isArray(r.vaultAccounts) ? normalizeAccountList(r.vaultAccounts) : void 0, activeId = typeof r.activeId === "string" ? r.activeId : void 0, route = typeof r.route === "string" ? r.route : DEFAULT_SIDEBAR_STATE.route, lastUpdated = typeof r.lastUpdated === "number" ? r.lastUpdated : 0, planType = typeof r.planType === "string" ? r.planType : void 0, credits = typeof r.credits === "number" ? r.credits : void 0, activeRouting = normalizeActiveRouting(r.activeRouting), stickyAssignments = normalizeStickyAssignments(r.stickyAssignments);
178
+ const fallbacks = normalizeAccountList(r.fallbacks), vaultAccounts = Array.isArray(r.vaultAccounts) ? normalizeAccountList(r.vaultAccounts) : void 0, activeId = typeof r.activeId === "string" ? r.activeId : void 0, route = typeof r.route === "string" ? r.route : DEFAULT_SIDEBAR_STATE.route, lastUpdated = typeof r.lastUpdated === "number" ? r.lastUpdated : 0, planType = typeof r.planType === "string" ? r.planType : void 0, activeRouting = normalizeActiveRouting(r.activeRouting), stickyAssignments = normalizeStickyAssignments(r.stickyAssignments);
157
179
  return {
158
180
  main,
159
181
  fallbacks,
@@ -163,7 +185,6 @@ export function normalizeSidebarState(raw) {
163
185
  ...activeRouting !== void 0 ? { activeRouting } : {},
164
186
  ...stickyAssignments !== void 0 ? { stickyAssignments } : {},
165
187
  ...planType !== void 0 ? { planType } : {},
166
- ...credits !== void 0 ? { credits } : {},
167
188
  ...r.accountPool === !0 ? { accountPool: !0 } : {},
168
189
  ...vaultAccounts !== void 0 ? { vaultAccounts } : {}
169
190
  };
@@ -491,12 +512,18 @@ function mergeSpendControl(fresher, older) {
491
512
  return;
492
513
  return older.spendControl;
493
514
  }
515
+ function fresherCredits(incoming, existing) {
516
+ if (!incoming || !existing)
517
+ return incoming ?? existing;
518
+ const at = (credits) => typeof credits.checkedAt === "number" && Number.isFinite(credits.checkedAt) ? credits.checkedAt : Number.NEGATIVE_INFINITY;
519
+ return at(existing) > at(incoming) ? existing : incoming;
520
+ }
494
521
  function mergeQuotaByWindow(incoming, existing) {
495
522
  if (!incoming)
496
523
  return existing;
497
524
  if (!existing)
498
525
  return incoming;
499
- const incomingAt = latestQuotaCheckedAt(incoming), existingAt = latestQuotaCheckedAt(existing), existingSnapshotIsFresher = existingAt !== void 0 && (incomingAt === void 0 || existingAt > incomingAt), primary = freshestWindow(incoming.primary, existing.primary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), secondary = freshestWindow(incoming.secondary, existing.secondary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), spendControl = existingSnapshotIsFresher ? mergeSpendControl(existing, incoming) : mergeSpendControl(incoming, existing), spendControlCleared = spendControl === void 0 && (incoming.spendControlCleared === !0 || existing.spendControlCleared === !0);
526
+ const incomingAt = latestQuotaCheckedAt(incoming), existingAt = latestQuotaCheckedAt(existing), existingSnapshotIsFresher = existingAt !== void 0 && (incomingAt === void 0 || existingAt > incomingAt), primary = freshestWindow(incoming.primary, existing.primary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), secondary = freshestWindow(incoming.secondary, existing.secondary, existingSnapshotIsFresher, incoming.checkedAt, existing.checkedAt), spendControl = existingSnapshotIsFresher ? mergeSpendControl(existing, incoming) : mergeSpendControl(incoming, existing), spendControlCleared = spendControl === void 0 && (incoming.spendControlCleared === !0 || existing.spendControlCleared === !0), credits = fresherCredits(incoming.credits, existing.credits);
500
527
  let checkedAt;
501
528
  for (const stamp of [
502
529
  finiteWindowCheckedAt(primary),
@@ -507,6 +534,7 @@ function mergeQuotaByWindow(incoming, existing) {
507
534
  const {
508
535
  spendControl: _incomingSpendControl,
509
536
  spendControlCleared: _incomingCleared,
537
+ credits: _incomingCredits,
510
538
  ...incomingRest
511
539
  } = incoming;
512
540
  return {
@@ -515,6 +543,7 @@ function mergeQuotaByWindow(incoming, existing) {
515
543
  secondary,
516
544
  ...spendControl !== void 0 ? { spendControl } : {},
517
545
  ...spendControlCleared ? { spendControlCleared: !0 } : {},
546
+ ...credits !== void 0 ? { credits } : {},
518
547
  checkedAt: checkedAt ?? incoming.checkedAt
519
548
  };
520
549
  }
@@ -22,7 +22,8 @@ export async function openControlDialog(dialog, initial, apply, active = () => !
22
22
  const choices = [
23
23
  ...section.items.filter((item) => item.actions.length > 0).map((item) => ({
24
24
  title: item.label,
25
- description: inlineDescription(item.status, item.detail),
25
+ description: inlineDescription(item.detail),
26
+ ...item.status ? { footer: item.status } : {},
26
27
  value: { itemId: item.id, actions: item.actions }
27
28
  })),
28
29
  ...section.actions.map((action) => ({