dsh-plugin-subscription-oauth 0.8.15 → 0.8.17

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.
@@ -300,20 +300,26 @@ export class ProviderController {
300
300
  // every startup would be noise.
301
301
  if (!await this.options.store.hasAny())
302
302
  return this.status();
303
- // Discovery resolves tokens to read the model list, but a resolve must never
304
- // open the browser: a transient network failure, or a token the endpoint refuses
305
- // while still being within its stored expiry, is not a signal to start a login.
306
- // Only an explicit user action (the connect button) opens the consent page.
303
+ // Discovery reads the live catalog on startup so the picker shows what the account
304
+ // actually serves without a manual refresh. A resolve must never open the browser:
305
+ // a refresh failure is recorded as the route's last error, never as a login prompt.
307
306
  const discovered = [];
308
307
  for (const account of Object.keys(await this.options.store.list())) {
308
+ // An unexpired token is used as-is; a lapsed one is refreshed in memory so the
309
+ // catalog still loads on startup. The refreshed token is not written back to the
310
+ // store here — a durable renewal still happens on the next real request.
309
311
  let session = await this.options.store.get(account);
310
312
  if (session === undefined)
311
313
  continue;
312
- // A token that is still within its stored expiry is used as-is; a lapsed one
313
- // is a silent skip (the route works, the catalog is just not refreshed).
314
314
  if (session.expiresAt <= Date.now()) {
315
- this.options.onWarn?.(`${spec.id} model discovery for ${account} skipped: token expired`);
316
- continue;
315
+ try {
316
+ const refreshed = await this.options.tokens.resolve(account, true);
317
+ session = { ...session, accessToken: refreshed.accessToken, expiresAt: refreshed.expiresAt };
318
+ }
319
+ catch {
320
+ this.options.onWarn?.(`${spec.id} model discovery for ${account} skipped: token refresh failed`);
321
+ continue;
322
+ }
317
323
  }
318
324
  const ids = await spec.discover(session.accessToken);
319
325
  if (ids !== undefined) {
@@ -30,14 +30,15 @@ export interface FastModeButtonInjected {
30
30
  /**
31
31
  * The session's model directory, when the host exposes one.
32
32
  *
33
- * Written when the button switches between a model and its fast variant. Absent on a
34
- * host without it, in which case the button renders nothing.
33
+ * Written when the button switches the model's `fast` rung. Absent on a host without
34
+ * it, in which case the button renders nothing.
35
35
  */
36
36
  modelDirectory?: {
37
- /** Select one model; resolves when the picker has accepted it. */
37
+ /** Select one model and its reasoning rung; resolves when the picker accepted it. */
38
38
  select?: (selection: {
39
39
  provider: string;
40
40
  model: string;
41
+ reasoningEffort?: string;
41
42
  }) => Promise<unknown>;
42
43
  } | undefined;
43
44
  }
@@ -57,16 +57,16 @@ export function FastModeButton({ call, isOwnProvider, modelDirectory, useProject
57
57
  }, [provider, model, isOwnProvider, call]);
58
58
  // A switch that landed clears the reason, so the marker follows the truth on screen.
59
59
  useEffect(() => { setFailure(undefined); }, [model]);
60
- if (pair === undefined || provider === undefined || modelDirectory?.select === undefined)
61
- return null;
62
- const active = pair.wireId === model;
63
- const target = active ? pair.baseId : pair.wireId;
64
- // Active without a base to return to would be a control that cannot switch anything off.
65
- if (target === undefined || target === model)
60
+ if (pair === undefined || provider === undefined || model === undefined || modelDirectory?.select === undefined)
66
61
  return null;
62
+ // Fast mode is the row's `fast` reasoning rung, so the model id never changes and the
63
+ // picker always shows the row the user can switch back to.
64
+ const active = (selection?.next ?? selection?.lastUsed)?.reasoningEffort === 'fast';
65
+ // The row the rung belongs to: what the picker selected, or the pair's own base.
66
+ const target = model;
67
67
  const base = active
68
68
  ? 'Fast mode is on; press to go back to the standard model'
69
- : 'Fast mode: switch to the fast variant of this model';
69
+ : 'Fast mode: switch to this model’s fast variant';
70
70
  const title = failure === undefined
71
71
  ? base
72
72
  : `${base}\nThe switch was refused — ${`${failure.code ?? ''} ${failure.message}`.trim()}`;
@@ -74,7 +74,10 @@ export function FastModeButton({ call, isOwnProvider, modelDirectory, useProject
74
74
  event.stopPropagation();
75
75
  setPending(true);
76
76
  setFailure(undefined);
77
- void Promise.resolve(modelDirectory.select?.({ provider, model: target }))
77
+ // Turning it off drops the rung, which the host then reads as the model's own
78
+ // default. The model id is never swapped, so this cannot strand the selection.
79
+ const next = active ? { provider, model: target } : { provider, model: target, reasoningEffort: 'fast' };
80
+ void Promise.resolve(modelDirectory.select?.(next))
78
81
  .then((result) => {
79
82
  // The directory reports its own outcome: a refused selection carries why.
80
83
  const outcome = result;
package/lib/client.js CHANGED
@@ -2815,11 +2815,10 @@ function FastModeButton({ call, isOwnProvider, modelDirectory, useProjection })
2815
2815
  (0, react.useEffect)(() => {
2816
2816
  setFailure(void 0);
2817
2817
  }, [model]);
2818
- if (pair === void 0 || provider === void 0 || modelDirectory?.select === void 0) return null;
2819
- const active = pair.wireId === model;
2820
- const target = active ? pair.baseId : pair.wireId;
2821
- if (target === void 0 || target === model) return null;
2822
- const base = active ? "Fast mode is on; press to go back to the standard model" : "Fast mode: switch to the fast variant of this model";
2818
+ if (pair === void 0 || provider === void 0 || model === void 0 || modelDirectory?.select === void 0) return null;
2819
+ const active = (selection?.next ?? selection?.lastUsed)?.reasoningEffort === "fast";
2820
+ const target = model;
2821
+ const base = active ? "Fast mode is on; press to go back to the standard model" : "Fast mode: switch to this model’s fast variant";
2823
2822
  const title = failure === void 0 ? base : `${base}\nThe switch was refused — ${`${failure.code ?? ""} ${failure.message}`.trim()}`;
2824
2823
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: fastModeCss }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2825
2824
  type: "button",
@@ -2835,10 +2834,15 @@ function FastModeButton({ call, isOwnProvider, modelDirectory, useProjection })
2835
2834
  event.stopPropagation();
2836
2835
  setPending(true);
2837
2836
  setFailure(void 0);
2838
- Promise.resolve(modelDirectory.select?.({
2837
+ const next = active ? {
2839
2838
  provider,
2840
2839
  model: target
2841
- })).then((result) => {
2840
+ } : {
2841
+ provider,
2842
+ model: target,
2843
+ reasoningEffort: "fast"
2844
+ };
2845
+ Promise.resolve(modelDirectory.select?.(next)).then((result) => {
2842
2846
  const outcome = result;
2843
2847
  if (outcome?.ok !== false) return;
2844
2848
  const code = outcome.error?.code;