@bridge4dev/runner 0.41.0 → 0.41.1

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.
@@ -719,6 +719,16 @@ class ClaudeSession {
719
719
  }
720
720
  emitRateLimitsFromInit(msg) {
721
721
  const init = msg;
722
+ // Probe against the installed CLI, 2026-08-15: `system:init` carries none of
723
+ // these keys at all — not `false`, ABSENT. The fields exist in the SDK's own
724
+ // typings, which is what this code trusted first, and the result was a panel
725
+ // stating «лимитов подписки нет» to an owner who has a subscription. Absence
726
+ // is «we do not know yet», and only a `rate_limit_event` can settle it.
727
+ if (init.rate_limits_available === undefined) {
728
+ if (typeof init.subscription_type === 'string')
729
+ this.ratePlanType = init.subscription_type;
730
+ return;
731
+ }
722
732
  this.rateLimitsAvailable = init.rate_limits_available === true;
723
733
  this.ratePlanType = typeof init.subscription_type === 'string' ? init.subscription_type : null;
724
734
  this.rateLimitWindows.clear();
@@ -747,25 +757,32 @@ class ClaudeSession {
747
757
  // without a percentage: the refusal is the fact, the number is decoration.
748
758
  // `allowed_warning` is NOT a refusal — the turn ran.
749
759
  const refused = info['status'] === 'rejected';
750
- if (typeof utilization !== 'number' && !refused)
751
- return;
752
- // A live event proves there IS a plan: the SDK only emits this for
753
- // claude.ai subscriptions. An account that reached `system:init` before
754
- // its profile scope resolved would otherwise stay `available: false`.
760
+ // NOT gated on the percentage. Probe against the installed CLI, 2026-08-15:
761
+ // every request emits this event, and `utilization` is usually absent —
762
+ // {status:'allowed', resetsAt:1786826400, rateLimitType:'five_hour', …}
763
+ // Requiring the number threw the whole event away, so the one source that
764
+ // does work was silently discarded and the panel had nothing to show. What
765
+ // the event always carries is WHICH window and WHEN it resets, and that is
766
+ // worth drawing on its own.
767
+ // A live event also proves there IS a plan: the SDK emits it only for
768
+ // claude.ai subscriptions.
755
769
  this.rateLimitsAvailable = true;
756
770
  const name = typeof info['rateLimitType'] === 'string' ? info['rateLimitType'] : 'five_hour';
757
771
  const key = rateWindowKey(name);
758
772
  // `resetsAt` here is epoch SECONDS, unlike `resets_at` on init which is
759
773
  // already ISO. Same fact, two encodings, one place that knows it.
760
774
  const resetsAt = typeof info['resetsAt'] === 'number' ? new Date(info['resetsAt'] * 1000).toISOString() : null;
761
- if (typeof utilization === 'number') {
762
- this.rateLimitWindows.set(name, {
763
- key,
764
- windowMinutes: RATE_WINDOW_MINUTES[key] ?? null,
765
- usedPercent: clampPercent(utilization),
766
- resetsAt,
767
- });
768
- }
775
+ // Keep whatever the previous event said about the percentage: the field
776
+ // comes and goes between events about the same window, and dropping it on
777
+ // the next silent one would make a number that was true flicker away.
778
+ const known = this.rateLimitWindows.get(name);
779
+ this.rateLimitWindows.set(name, {
780
+ key,
781
+ windowMinutes: RATE_WINDOW_MINUTES[key] ?? null,
782
+ usedPercent: typeof utilization === 'number' ? clampPercent(utilization) : (known?.usedPercent ?? null),
783
+ resetsAt: resetsAt ?? known?.resetsAt ?? null,
784
+ status: typeof info['status'] === 'string' ? info['status'] : null,
785
+ });
769
786
  if (refused)
770
787
  this.limitBlockPending = true;
771
788
  this.emitRateLimits(refused ? { key, resetsAt } : null);
@@ -1432,7 +1432,7 @@ class CodexSession {
1432
1432
  haystack.includes('usagelimitreached');
1433
1433
  if (!refused)
1434
1434
  return null;
1435
- const fullest = this.rateLimitWindows.reduce((worst, window) => worst === null || window.usedPercent > worst.usedPercent ? window : worst, null);
1435
+ const fullest = this.rateLimitWindows.reduce((worst, window) => worst === null || (window.usedPercent ?? 0) > (worst.usedPercent ?? 0) ? window : worst, null);
1436
1436
  return { key: fullest?.key ?? 'other', resetsAt: fullest?.resetsAt ?? null };
1437
1437
  }
1438
1438
  onTurnCompleted(params) {
@@ -149,10 +149,19 @@ export interface AgentRateLimitWindow {
149
149
  key: 'five_hour' | 'seven_day' | 'other';
150
150
  /** Length of the window, when the provider states it. */
151
151
  windowMinutes: number | null;
152
- /** 0–100. What is SPENT, not what is left — both CLIs report it that way. */
153
- usedPercent: number;
152
+ /**
153
+ * 0–100, SPENT rather than left — and **nullable**.
154
+ *
155
+ * Claude's live event carries the window and its reset time on every request
156
+ * but usually omits the percentage (probe, 2026-08-15). A window we can date
157
+ * but not measure is still worth showing; requiring the number here is what
158
+ * made the panel say «no plan» to an account that has one.
159
+ */
160
+ usedPercent: number | null;
154
161
  /** Absolute moment, ISO 8601. Null when the provider did not say. */
155
162
  resetsAt: string | null;
163
+ /** `allowed` · `allowed_warning` · `rejected`, when the provider says. */
164
+ status?: string | null;
156
165
  }
157
166
  /**
158
167
  * The account's plan usage, as one snapshot (#279, #258).
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const RUNNER_VERSION = "0.41.0";
1
+ export declare const RUNNER_VERSION = "0.41.1";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Kept in sync with package.json by the release script (manual for now).
2
- export const RUNNER_VERSION = '0.41.0';
2
+ export const RUNNER_VERSION = '0.41.1';
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge4dev/runner",
3
- "version": "0.41.0",
3
+ "version": "0.41.1",
4
4
  "description": "DevBridge dev runner — connects a dev server to DevBridge and runs agent sessions (Claude Code / Codex)",
5
5
  "homepage": "https://bridge4.dev",
6
6
  "license": "MIT",