github-issue-tower-defence-management 1.195.0 → 1.196.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 (26) hide show
  1. package/.github/workflows/codeql.yml +2 -2
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +2 -2
  4. package/bin/adapter/entry-points/cli/fleetConfig.js +2 -0
  5. package/bin/adapter/entry-points/cli/fleetConfig.js.map +1 -1
  6. package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js +1 -1
  7. package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js.map +1 -1
  8. package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js +17 -2
  9. package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js.map +1 -1
  10. package/bin/domain/usecases/OauthTokenSelectUseCase.js +18 -10
  11. package/bin/domain/usecases/OauthTokenSelectUseCase.js.map +1 -1
  12. package/package.json +2 -2
  13. package/src/adapter/entry-points/cli/fleetConfig.test.ts +33 -1
  14. package/src/adapter/entry-points/cli/fleetConfig.ts +16 -0
  15. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +78 -0
  16. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +2 -0
  17. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +137 -17
  18. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +30 -2
  19. package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +99 -0
  20. package/src/domain/usecases/OauthTokenSelectUseCase.ts +30 -5
  21. package/types/adapter/entry-points/cli/fleetConfig.d.ts.map +1 -1
  22. package/types/adapter/entry-points/handlers/OauthTokenSelectHandler.d.ts.map +1 -1
  23. package/types/domain/usecases/LiveSessionOauthTokenSelectUseCase.d.ts +3 -0
  24. package/types/domain/usecases/LiveSessionOauthTokenSelectUseCase.d.ts.map +1 -1
  25. package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +7 -1
  26. package/types/domain/usecases/OauthTokenSelectUseCase.d.ts.map +1 -1
@@ -73,7 +73,7 @@ jobs:
73
73
 
74
74
  # Initializes the CodeQL tools for scanning.
75
75
  - name: Initialize CodeQL
76
- uses: github/codeql-action/init@v4.37.7
76
+ uses: github/codeql-action/init@v4.37.8
77
77
  with:
78
78
  languages: ${{ matrix.language }}
79
79
  build-mode: ${{ matrix.build-mode }}
@@ -102,6 +102,6 @@ jobs:
102
102
  exit 1
103
103
 
104
104
  - name: Perform CodeQL Analysis
105
- uses: github/codeql-action/analyze@v4.37.7
105
+ uses: github/codeql-action/analyze@v4.37.8
106
106
  with:
107
107
  category: '/language:${{matrix.language}}'
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [1.196.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.195.0...v1.196.0) (2026-08-29)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **deps:** update dependency googleapis to v176 ([#1831](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1831)) ([b03e898](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/b03e898f85d90f918d31b1efdf2acd2587962d5e))
7
+
8
+
9
+ ### Features
10
+
11
+ * **select-token:** apply stricter token selection thresholds for CL scripts ([#1826](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1826)) ([6301773](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/63017733898c6336b8d0776fd2b7846b778d91bb)), closes [HiromiShikata/umino-corporait-operation#30998](https://github.com/HiromiShikata/umino-corporait-operation/issues/30998)
12
+
1
13
  # [1.195.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.194.2...v1.195.0) (2026-08-29)
2
14
 
3
15
 
package/README.md CHANGED
@@ -159,9 +159,9 @@ storyProgressCommentEnabled: false
159
159
 
160
160
  The `checkIssueReviewReadiness` sub-command lets an agent self-check whether an issue is currently review-ready. It does NOT change the issue Status field and does NOT post any comment. It writes a single JSON line to stdout of the shape `{ "reviewReady": boolean, "rejections": [{ "type": string, "detail": string }] }` and exits 0 on a successful evaluation regardless of readiness; a non-zero exit indicates an operational error (auth failure, network error). The rejection types include: `ISSUE_NOT_FOUND`, `NO_REPORT_FROM_AGENT_BOT`, `REPORT_HAS_NEXT_STEP`, `PULL_REQUEST_NOT_FOUND`, `PULL_REQUEST_IS_DRAFT`, `PULL_REQUEST_CONFLICTED`, `ANY_CI_JOB_FAILED_OR_IN_PROGRESS`, `REQUIRED_CI_JOB_NEVER_STARTED`, `ANY_REVIEW_COMMENT_NOT_RESOLVED`, and `MULTIPLE_PULL_REQUESTS_FOUND`. The `--projectUrl` option is optional; when omitted the command still runs using only the issue URL.
161
161
 
162
- The `selectOauthToken` sub-command reads the same per-token rate-limit cache that the `startDaemon` proxy writes (see "Claude OAuth Token Rotation" below) and prints exactly one token string to stdout so a caller can choose an appropriate token before launching Claude Code. It is read-only: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. Selection runs in two stages. First, a candidate filter keeps tokens whose 5-hour window is at least 25% free (5-hour utilization at most 0.75) AND whose 7-day window is at least 3% free (7-day utilization at most 0.97), where "% free" is `1 - utilization`. A token with no cache file, or whose window reset epoch has already passed, is treated as fully free (utilization 0) for these checks. The filter additionally excludes any token carrying a non-expired reactive `seven_day_fable` rejection marker — set when a `fable`-model request on that token was rejected with HTTP 429 (see "Claude OAuth Token Rotation" below) — because these interactive sessions run on the `fable` model; a token without the marker is treated as `fable`-usable, and the marker is ignored once its stored reset epoch has passed. Second, among the surviving candidates it selects the single token whose 7-day window reset epoch is nearest in the future (soonest reset), so weekly quota that would otherwise reset unused is consumed first; a candidate with no active 7-day window is treated as having the farthest reset (now + 7 days) and therefore sorts last. Each token entry may carry an optional `selectionWeight` (a positive number, default `1`); when the eligible candidates carry differing weights the selection among them becomes weighted-random by that weight, so a token with a smaller weight is chosen proportionally less often, while a token that is the only eligible candidate is always chosen regardless of its weight. When every eligible candidate shares the same weight (the default), the deterministic soonest-reset selection above is used unchanged. The selected token string is written to stdout (pipeable) and the per-candidate decision trace is written to stderr. When no token passes the filter, nothing is written to stdout and the command exits non-zero with an explanatory message on stderr. The token-list path comes from `--tokenListJsonPath` or the `CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH` environment variable; the cache directory comes from `--cacheDir` or the `TDPM_RATELIMIT_CACHE_DIR` environment variable, defaulting to `${XDG_CACHE_HOME:-~/.cache}/tdpm/ratelimit`.
162
+ The `selectOauthToken` sub-command reads the same per-token rate-limit cache that the `startDaemon` proxy writes (see "Claude OAuth Token Rotation" below) and prints exactly one token string to stdout so a caller can choose an appropriate token before launching Claude Code. It is read-only: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. Selection runs in two stages. First, a candidate filter keeps tokens whose 5-hour window is at least 60% free (5-hour utilization at most 0.40) AND whose 7-day window is at least 14% free (7-day utilization at most 0.86), where "% free" is `1 - utilization`. A token with no cache file, or whose window reset epoch has already passed, is treated as fully free (utilization 0) for these checks. The filter additionally excludes any token carrying a non-expired reactive `seven_day_fable` rejection marker — set when a `fable`-model request on that token was rejected with HTTP 429 (see "Claude OAuth Token Rotation" below) — because these interactive sessions run on the `fable` model; a token without the marker is treated as `fable`-usable, and the marker is ignored once its stored reset epoch has passed. Second, among the surviving candidates it selects the single token whose 7-day window reset epoch is nearest in the future (soonest reset), so weekly quota that would otherwise reset unused is consumed first; a candidate with no active 7-day window is treated as having the farthest reset (now + 7 days) and therefore sorts last. Each token entry may carry an optional `selectionWeight` (a positive number, default `1`); when the eligible candidates carry differing weights the selection among them becomes weighted-random by that weight, so a token with a smaller weight is chosen proportionally less often, while a token that is the only eligible candidate is always chosen regardless of its weight. When every eligible candidate shares the same weight (the default), the deterministic soonest-reset selection above is used unchanged. The selected token string is written to stdout (pipeable) and the per-candidate decision trace is written to stderr. When no token passes the filter, nothing is written to stdout and the command exits non-zero with an explanatory message on stderr. The token-list path comes from `--tokenListJsonPath` or the `CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH` environment variable; the cache directory comes from `--cacheDir` or the `TDPM_RATELIMIT_CACHE_DIR` environment variable, defaulting to `${XDG_CACHE_HOME:-~/.cache}/tdpm/ratelimit`.
163
163
 
164
- The `selectLiveSessionOauthToken` sub-command picks a token for a new live interactive Claude Code session a human is about to start, so that concurrent interactive sessions spread across distinct tokens instead of stacking onto the same one. It loads the token list and reads the same per-token rate-limit cache as `selectOauthToken`, and it is read-only in exactly the same way: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. It applies the identical rate-limit eligibility filter as `selectOauthToken` (5-hour window at least 25% free AND 7-day window at least 3% free, with a missing cache file or an expired window treated as fully free, and any token carrying a non-expired reactive `seven_day_fable` rejection marker excluded). It additionally measures current live occupancy per token by scanning running Claude Code processes on the local Linux host: for each process under `/proc` it reads the NUL-separated `/proc/<pid>/environ` and, when the process is a Claude Code process, takes its `CLAUDE_CODE_OAUTH_TOKEN` and `CLAUDE_CODE_SESSION_ID`. A token's occupancy is the number of distinct `CLAUDE_CODE_SESSION_ID` values bound to it, so child processes that inherit one session id count once. Processes without `CLAUDE_CODE_OAUTH_TOKEN` (for example API-key sessions) are ignored, and a process whose environ cannot be read is skipped. Among the eligible tokens the selection is deterministic and reset-first. Each eligible token is given a concurrent session limit of `maxConcurrentSessionCount` (default `10`), scaled by its optional `selectionWeight` (a positive number, default `1`). That limit is held at its full value while the free share of the token's 5-hour window is at or above `fullSpeedFiveHourFreeRatio` (default `0.5`), and is tapered linearly in proportion to the free share below that point, never dropping under `1`. The 5-hour window is the only window that lowers the limit: it is the one that runs out within a single working stretch, and a session that hits it mid-flight has to be restarted, which costs more than it saves. The 7-day window never lowers the limit, so a weekly allowance that is about to expire is drained at full speed rather than discarded unused; the 7-day window is instead what orders the candidates. The token whose 7-day window resets soonest among those still under their limit is selected, so allowance that is about to expire is consumed before the reset discards it; ties are broken by the fewer live sessions. Both tuning numbers are read from the fleet-wide config file named by `--fleetConfigFilePath` or the `TDPM_FLEET_CONFIG` environment variable, under a `liveSessionOauthTokenSelection` mapping; a key the file omits keeps its built-in value, while an unreadable file or an out-of-range value is reported as an error rather than silently ignored. A token at its limit is skipped in favour of the next soonest-resetting token that still has room, and when every eligible token is at its limit the soonest-resetting one is returned rather than none. A token that is the only eligible candidate is always chosen regardless of its weight or occupancy. The selected token string is written to stdout (pipeable) and the per-candidate decision trace is written to stderr; when no token passes the filter, nothing is written to stdout and the command exits non-zero with an explanatory message on stderr. The token-list path and cache directory are resolved exactly as for `selectOauthToken`. Because occupancy is read from `/proc/<pid>/environ`, this sub-command is Linux-specific.
164
+ The `selectLiveSessionOauthToken` sub-command picks a token for a new live interactive Claude Code session a human is about to start, so that concurrent interactive sessions spread across distinct tokens instead of stacking onto the same one. It loads the token list and reads the same per-token rate-limit cache as `selectOauthToken`, and it is read-only in exactly the same way: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. It applies a base rate-limit eligibility filter (5-hour window at least 25% free AND 7-day window at least 3% free, with a missing cache file or an expired window treated as fully free, and any token carrying a non-expired reactive `seven_day_fable` rejection marker excluded), and additionally excludes any token whose 5-hour window is less than `minFiveHourFreeRatio` (default 60%) free or whose 7-day window is less than `minSevenDayFreeRatio` (default 14%) free; these stricter thresholds prevent the `cl` script from starting a new interactive session on a token that is nearly exhausted. It additionally measures current live occupancy per token by scanning running Claude Code processes on the local Linux host: for each process under `/proc` it reads the NUL-separated `/proc/<pid>/environ` and, when the process is a Claude Code process, takes its `CLAUDE_CODE_OAUTH_TOKEN` and `CLAUDE_CODE_SESSION_ID`. A token's occupancy is the number of distinct `CLAUDE_CODE_SESSION_ID` values bound to it, so child processes that inherit one session id count once. Processes without `CLAUDE_CODE_OAUTH_TOKEN` (for example API-key sessions) are ignored, and a process whose environ cannot be read is skipped. Among the eligible tokens the selection is deterministic and reset-first. Each eligible token is given a concurrent session limit of `maxConcurrentSessionCount` (default `10`), scaled by its optional `selectionWeight` (a positive number, default `1`). That limit is held at its full value while the free share of the token's 5-hour window is at or above `fullSpeedFiveHourFreeRatio` (default `0.5`), and is tapered linearly in proportion to the free share below that point, never dropping under `1`. The 5-hour window is the only window that lowers the limit: it is the one that runs out within a single working stretch, and a session that hits it mid-flight has to be restarted, which costs more than it saves. The 7-day window never lowers the limit, so a weekly allowance that is about to expire is drained at full speed rather than discarded unused; the 7-day window is instead what orders the candidates. The token whose 7-day window resets soonest among those still under their limit is selected, so allowance that is about to expire is consumed before the reset discards it; ties are broken by the fewer live sessions. All four tuning numbers (`maxConcurrentSessionCount`, `fullSpeedFiveHourFreeRatio`, `minFiveHourFreeRatio`, `minSevenDayFreeRatio`) are read from the fleet-wide config file named by `--fleetConfigFilePath` or the `TDPM_FLEET_CONFIG` environment variable, under a `liveSessionOauthTokenSelection` mapping; a key the file omits keeps its built-in value, while an unreadable file or an out-of-range value is reported as an error rather than silently ignored. A token at its limit is skipped in favour of the next soonest-resetting token that still has room, and when every eligible token is at its limit the soonest-resetting one is returned rather than none. A token that is the only eligible candidate is always chosen regardless of its weight or occupancy. The selected token string is written to stdout (pipeable) and the per-candidate decision trace is written to stderr; when no token passes the filter, nothing is written to stdout and the command exits non-zero with an explanatory message on stderr. The token-list path and cache directory are resolved exactly as for `selectOauthToken`. Because occupancy is read from `/proc/<pid>/environ`, this sub-command is Linux-specific.
165
165
 
166
166
  The `countInTmuxByHumanSessionsPerToken` sub-command reports, per Claude Code OAuth token, how many live interactive sessions running under that token belong to an issue that is currently in the GitHub Project Status `In Tmux by human`. It is read-only: it never starts the proxy, never mutates any cache file, and never writes any token anywhere. It enumerates live interactive sessions by scanning running processes on the local Linux host: for each process under `/proc` it reads the NUL-separated `/proc/<pid>/cmdline` and selects only processes launched for an interactive session, identified by a `--name <issue-url>` argument whose value is an HTTP or HTTPS URL; `Take ownership` background spawns (which have no `--name` argument) are excluded. For each selected process it reads `/proc/<pid>/environ` and takes its `CLAUDE_CODE_OAUTH_TOKEN` and `CLAUDE_CODE_SESSION_ID`; a process missing either is skipped, and a process whose files cannot be read is skipped. It then loads the project's issues with their Status (reusing the same on-disk issue cache the daemon uses), keeps only the sessions whose `--name` issue URL maps to an open issue in Status `In Tmux by human`, and counts the distinct `CLAUDE_CODE_SESSION_ID` values per token so child processes that inherit one session id count once. It writes one tab-separated line per token to stdout in the form `<tokenName>\t<count>` (the token name comes from the token list; the raw token value is never printed), and writes the per-run decision trace to stderr. The token-list path comes from `--tokenListJsonPath`, the `claudeCodeOauthTokenListJsonPath` config value, or the `CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH` environment variable. Because occupancy is read from `/proc/<pid>/cmdline` and `/proc/<pid>/environ`, this sub-command is Linux-specific.
167
167
 
@@ -95,6 +95,8 @@ const loadLiveSessionOauthTokenSelectionSettings = (fleetConfigFilePath) => {
95
95
  return {
96
96
  maxConcurrentSessionCount: readBoundedNumber(section, 'maxConcurrentSessionCount', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.maxConcurrentSessionCount, (value) => Number.isInteger(value) && value >= 1, 'integer of at least 1'),
97
97
  fullSpeedFiveHourFreeRatio: readBoundedNumber(section, 'fullSpeedFiveHourFreeRatio', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.fullSpeedFiveHourFreeRatio, (value) => value > 0 && value <= 1, 'above 0 and at most 1'),
98
+ minFiveHourFreeRatio: readBoundedNumber(section, 'minFiveHourFreeRatio', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minFiveHourFreeRatio, (value) => value > 0 && value <= 1, 'above 0 and at most 1'),
99
+ minSevenDayFreeRatio: readBoundedNumber(section, 'minSevenDayFreeRatio', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minSevenDayFreeRatio, (value) => value > 0 && value <= 1, 'above 0 and at most 1'),
98
100
  };
99
101
  };
100
102
  exports.loadLiveSessionOauthTokenSelectionSettings = loadLiveSessionOauthTokenSelectionSettings;
@@ -1 +1 @@
1
- {"version":3,"file":"fleetConfig.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fleetConfig.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":"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"}
@@ -77,7 +77,7 @@ class OauthTokenSelectHandler {
77
77
  selectionWeight: selectionWeight ?? OauthTokenSelectUseCase_1.DEFAULT_SELECTION_WEIGHT,
78
78
  };
79
79
  });
80
- const result = this.useCase.run(candidates, input.nowEpochSeconds, this.random);
80
+ const result = this.useCase.run(candidates, input.nowEpochSeconds, this.random, OauthTokenSelectUseCase_1.CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS);
81
81
  return {
82
82
  selectedToken: result.selected?.token ?? null,
83
83
  selectedName: result.selected?.name ?? null,
@@ -1 +1 @@
1
- {"version":3,"file":"OauthTokenSelectHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OauthTokenSelectHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,8 @@ const OauthTokenSelectUseCase_1 = require("./OauthTokenSelectUseCase");
5
5
  exports.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS = {
6
6
  maxConcurrentSessionCount: 10,
7
7
  fullSpeedFiveHourFreeRatio: 0.5,
8
+ minFiveHourFreeRatio: 0.6,
9
+ minSevenDayFreeRatio: 0.14,
8
10
  };
9
11
  const liveSessionConcurrentLimitOf = (fiveHourFreeRatio, selectionWeight, settings) => {
10
12
  const fiveHourThrottleFactor = Math.min(fiveHourFreeRatio / settings.fullSpeedFiveHourFreeRatio, 1);
@@ -23,6 +25,7 @@ class LiveSessionOauthTokenSelectUseCase {
23
25
  const rateLimitMetric = rateLimitResult.metrics[index];
24
26
  const liveSessionCount = liveSessionCountByToken.get(candidate.token) ?? 0;
25
27
  const concurrentSessionLimit = (0, exports.liveSessionConcurrentLimitOf)(rateLimitMetric.fiveHourFreeRatio, (0, OauthTokenSelectUseCase_1.selectionWeightOf)(candidate), settings);
28
+ const exclusionReason = this.liveSessionExclusionReason(rateLimitMetric.exclusionReason, rateLimitMetric.fiveHourFreeRatio, rateLimitMetric.sevenDayFreeRatio, settings);
26
29
  return {
27
30
  candidate,
28
31
  metric: {
@@ -33,8 +36,8 @@ class LiveSessionOauthTokenSelectUseCase {
33
36
  liveSessionCount,
34
37
  concurrentSessionLimit,
35
38
  hasConcurrencyHeadroom: liveSessionCount < concurrentSessionLimit,
36
- eligible: rateLimitMetric.eligible,
37
- exclusionReason: rateLimitMetric.exclusionReason,
39
+ eligible: exclusionReason === null,
40
+ exclusionReason,
38
41
  selectionWeight: (0, OauthTokenSelectUseCase_1.selectionWeightOf)(candidate),
39
42
  },
40
43
  };
@@ -49,6 +52,18 @@ class LiveSessionOauthTokenSelectUseCase {
49
52
  : bestEntry);
50
53
  return { selected: selected.candidate, metrics };
51
54
  };
55
+ this.liveSessionExclusionReason = (rateLimitExclusionReason, fiveHourFreeRatio, sevenDayFreeRatio, settings) => {
56
+ if (rateLimitExclusionReason !== null) {
57
+ return rateLimitExclusionReason;
58
+ }
59
+ if (fiveHourFreeRatio < settings.minFiveHourFreeRatio) {
60
+ return `5h window only ${Math.round(fiveHourFreeRatio * 100)}% free (requires >= ${Math.round(settings.minFiveHourFreeRatio * 100)}% for live session selection)`;
61
+ }
62
+ if (sevenDayFreeRatio < settings.minSevenDayFreeRatio) {
63
+ return `7d window only ${Math.round(sevenDayFreeRatio * 100)}% free (requires >= ${Math.round(settings.minSevenDayFreeRatio * 100)}% for live session selection)`;
64
+ }
65
+ return null;
66
+ };
52
67
  this.preferred = (candidateMetric, incumbentMetric) => {
53
68
  if (candidateMetric.hasConcurrencyHeadroom !==
54
69
  incumbentMetric.hasConcurrencyHeadroom) {
@@ -1 +1 @@
1
- {"version":3,"file":"LiveSessionOauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LiveSessionOauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":";;;AACA,uEAImC;AAStB,QAAA,mDAAmD,GAC9D;IACE,yBAAyB,EAAE,EAAE;IAC7B,0BAA0B,EAAE,GAAG;IAC/B,oBAAoB,EAAE,GAAG;IACzB,oBAAoB,EAAE,IAAI;CAC3B,CAAC;AAEG,MAAM,4BAA4B,GAAG,CAC1C,iBAAyB,EACzB,eAAuB,EACvB,QAAgD,EACxC,EAAE;IACV,MAAM,sBAAsB,GAAG,IAAI,CAAC,GAAG,CACrC,iBAAiB,GAAG,QAAQ,CAAC,0BAA0B,EACvD,CAAC,CACF,CAAC;IACF,OAAO,IAAI,CAAC,GAAG,CACb,IAAI,CAAC,KAAK,CACR,QAAQ,CAAC,yBAAyB;QAChC,eAAe;QACf,sBAAsB,CACzB,EACD,CAAC,CACF,CAAC;AACJ,CAAC,CAAC;AAjBW,QAAA,4BAA4B,gCAiBvC;AAoBF,MAAa,kCAAkC;IAC7C,YACmB,yBAAkD,IAAI,iDAAuB,EAAE;QAA/E,2BAAsB,GAAtB,sBAAsB,CAAyD;QAGlG,QAAG,GAAG,CACJ,UAAiC,EACjC,YAAiC,EACjC,eAAuB,EACvB,QAAgD,EACb,EAAE;YACrC,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CACrD,UAAU,EACV,eAAe,EACf,GAAG,EAAE,CAAC,CAAC,CACR,CAAC;YACF,MAAM,uBAAuB,GAAG,IAAI,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAC;YAE3E,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE;gBACpD,MAAM,eAAe,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACvD,MAAM,gBAAgB,GACpB,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACpD,MAAM,sBAAsB,GAAG,IAAA,oCAA4B,EACzD,eAAe,CAAC,iBAAiB,EACjC,IAAA,2CAAiB,EAAC,SAAS,CAAC,EAC5B,QAAQ,CACT,CAAC;gBACF,MAAM,eAAe,GAAG,IAAI,CAAC,0BAA0B,CACrD,eAAe,CAAC,eAAe,EAC/B,eAAe,CAAC,iBAAiB,EACjC,eAAe,CAAC,iBAAiB,EACjC,QAAQ,CACT,CAAC;gBACF,OAAO;oBACL,SAAS;oBACT,MAAM,EAAE;wBACN,IAAI,EAAE,eAAe,CAAC,IAAI;wBAC1B,iBAAiB,EAAE,eAAe,CAAC,iBAAiB;wBACpD,iBAAiB,EAAE,eAAe,CAAC,iBAAiB;wBACpD,gBAAgB,EAAE,eAAe,CAAC,gBAAgB;wBAClD,gBAAgB;wBAChB,sBAAsB;wBACtB,sBAAsB,EAAE,gBAAgB,GAAG,sBAAsB;wBACjE,QAAQ,EAAE,eAAe,KAAK,IAAI;wBAClC,eAAe;wBACf,eAAe,EAAE,IAAA,2CAAiB,EAAC,SAAS,CAAC;qBAC9C;iBACF,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACvD,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAEpE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACrC,CAAC;YAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,CAC3D,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;gBACnD,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,SAAS,CACd,CAAC;YAEF,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;QACnD,CAAC,CAAC;QAEM,+BAA0B,GAAG,CACnC,wBAAuC,EACvC,iBAAyB,EACzB,iBAAyB,EACzB,QAAgD,EACjC,EAAE;YACjB,IAAI,wBAAwB,KAAK,IAAI,EAAE,CAAC;gBACtC,OAAO,wBAAwB,CAAC;YAClC,CAAC;YACD,IAAI,iBAAiB,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC;gBACtD,OAAO,kBAAkB,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,GAAG,CAAC,uBAAuB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,oBAAoB,GAAG,GAAG,CAAC,+BAA+B,CAAC;YACpK,CAAC;YACD,IAAI,iBAAiB,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC;gBACtD,OAAO,kBAAkB,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,GAAG,CAAC,uBAAuB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,oBAAoB,GAAG,GAAG,CAAC,+BAA+B,CAAC;YACpK,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QAEM,cAAS,GAAG,CAClB,eAAsD,EACtD,eAAsD,EAC7C,EAAE;YACX,IACE,eAAe,CAAC,sBAAsB;gBACtC,eAAe,CAAC,sBAAsB,EACtC,CAAC;gBACD,OAAO,eAAe,CAAC,sBAAsB,CAAC;YAChD,CAAC;YACD,IAAI,eAAe,CAAC,gBAAgB,KAAK,eAAe,CAAC,gBAAgB,EAAE,CAAC;gBAC1E,OAAO,CACL,eAAe,CAAC,gBAAgB,GAAG,eAAe,CAAC,gBAAgB,CACpE,CAAC;YACJ,CAAC;YACD,OAAO,eAAe,CAAC,gBAAgB,GAAG,eAAe,CAAC,gBAAgB,CAAC;QAC7E,CAAC,CAAC;QAEM,4BAAuB,GAAG,CAChC,YAAiC,EACZ,EAAE;YACvB,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAuB,CAAC;YAC1D,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;gBACvC,MAAM,WAAW,GACf,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;gBACjE,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;gBACxC,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;YAC/C,KAAK,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,kBAAkB,CAAC,OAAO,EAAE,EAAE,CAAC;gBAChE,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;YAC5C,CAAC;YACD,OAAO,YAAY,CAAC;QACtB,CAAC,CAAC;IAlHC,CAAC;CAmHL;AAtHD,gFAsHC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OauthTokenSelectUseCase = exports.selectWeightedCandidate = exports.sevenDayUrgencyFactor = exports.MIN_HOURS_TO_RESET = exports.SEVEN_DAY_WINDOW_HOURS = exports.SEVEN_DAY_MIN_FREE_RATIO = exports.FIVE_HOUR_MIN_FREE_RATIO = exports.selectionWeightOf = exports.DEFAULT_SELECTION_WEIGHT = void 0;
3
+ exports.OauthTokenSelectUseCase = exports.selectWeightedCandidate = exports.sevenDayUrgencyFactor = exports.MIN_HOURS_TO_RESET = exports.SEVEN_DAY_WINDOW_HOURS = exports.CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS = exports.DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS = exports.SEVEN_DAY_MIN_FREE_RATIO = exports.FIVE_HOUR_MIN_FREE_RATIO = exports.selectionWeightOf = exports.DEFAULT_SELECTION_WEIGHT = void 0;
4
4
  exports.DEFAULT_SELECTION_WEIGHT = 1;
5
5
  const selectionWeightOf = (candidate) => {
6
6
  const weight = candidate.selectionWeight ?? exports.DEFAULT_SELECTION_WEIGHT;
@@ -11,6 +11,14 @@ const SECONDS_PER_DAY = 86400;
11
11
  const SEVEN_DAYS_IN_SECONDS = 7 * SECONDS_PER_DAY;
12
12
  exports.FIVE_HOUR_MIN_FREE_RATIO = 0.25;
13
13
  exports.SEVEN_DAY_MIN_FREE_RATIO = 0.03;
14
+ exports.DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS = {
15
+ fiveHourMinFreeRatio: exports.FIVE_HOUR_MIN_FREE_RATIO,
16
+ sevenDayMinFreeRatio: exports.SEVEN_DAY_MIN_FREE_RATIO,
17
+ };
18
+ exports.CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS = {
19
+ fiveHourMinFreeRatio: 0.6,
20
+ sevenDayMinFreeRatio: 0.14,
21
+ };
14
22
  exports.SEVEN_DAY_WINDOW_HOURS = 168;
15
23
  exports.MIN_HOURS_TO_RESET = 1;
16
24
  const SECONDS_PER_HOUR = 3600;
@@ -41,9 +49,9 @@ const selectWeightedCandidate = (eligible, weightOf, deterministicBest, random)
41
49
  exports.selectWeightedCandidate = selectWeightedCandidate;
42
50
  class OauthTokenSelectUseCase {
43
51
  constructor() {
44
- this.run = (candidates, nowEpochSeconds, random = Math.random) => {
52
+ this.run = (candidates, nowEpochSeconds, random = Math.random, thresholds = exports.DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS) => {
45
53
  const evaluated = candidates.map((candidate) => {
46
- const evaluatedMetric = this.evaluate(candidate, nowEpochSeconds);
54
+ const evaluatedMetric = this.evaluate(candidate, nowEpochSeconds, thresholds);
47
55
  return {
48
56
  candidate,
49
57
  metric: {
@@ -66,11 +74,11 @@ class OauthTokenSelectUseCase {
66
74
  const selected = (0, exports.selectWeightedCandidate)(eligible, (entry) => entry.metric.drawWeight, deterministicBest, random);
67
75
  return { selected: selected.candidate, metrics };
68
76
  };
69
- this.evaluate = (candidate, nowEpochSeconds) => {
77
+ this.evaluate = (candidate, nowEpochSeconds, thresholds) => {
70
78
  const fiveHourFreeRatio = this.fiveHourFreeRatio(candidate.snapshot, nowEpochSeconds);
71
79
  const sevenDayFreeRatio = this.sevenDayFreeRatio(candidate.snapshot, nowEpochSeconds);
72
80
  const sevenDayEndEpoch = this.sevenDayEndEpoch(candidate.snapshot, nowEpochSeconds);
73
- const exclusionReason = this.exclusionReason(candidate.subscriptionDisabled, candidate.unifiedRejected, candidate.fableRejected, fiveHourFreeRatio, sevenDayFreeRatio);
81
+ const exclusionReason = this.exclusionReason(candidate.subscriptionDisabled, candidate.unifiedRejected, candidate.fableRejected, fiveHourFreeRatio, sevenDayFreeRatio, thresholds);
74
82
  return {
75
83
  name: candidate.name,
76
84
  fiveHourFreeRatio,
@@ -80,7 +88,7 @@ class OauthTokenSelectUseCase {
80
88
  exclusionReason,
81
89
  };
82
90
  };
83
- this.exclusionReason = (subscriptionDisabled, unifiedRejected, fableRejected, fiveHourFreeRatio, sevenDayFreeRatio) => {
91
+ this.exclusionReason = (subscriptionDisabled, unifiedRejected, fableRejected, fiveHourFreeRatio, sevenDayFreeRatio, thresholds) => {
84
92
  if (subscriptionDisabled) {
85
93
  return 'organization has disabled Claude subscription access for Claude Code';
86
94
  }
@@ -90,11 +98,11 @@ class OauthTokenSelectUseCase {
90
98
  if (fableRejected) {
91
99
  return 'fable weekly limit exhausted (a fable request was rejected with HTTP 429)';
92
100
  }
93
- if (fiveHourFreeRatio < exports.FIVE_HOUR_MIN_FREE_RATIO) {
94
- return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(exports.FIVE_HOUR_MIN_FREE_RATIO)}%)`;
101
+ if (fiveHourFreeRatio < thresholds.fiveHourMinFreeRatio) {
102
+ return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(thresholds.fiveHourMinFreeRatio)}%)`;
95
103
  }
96
- if (sevenDayFreeRatio < exports.SEVEN_DAY_MIN_FREE_RATIO) {
97
- return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(exports.SEVEN_DAY_MIN_FREE_RATIO)}%)`;
104
+ if (sevenDayFreeRatio < thresholds.sevenDayMinFreeRatio) {
105
+ return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(thresholds.sevenDayMinFreeRatio)}%)`;
98
106
  }
99
107
  return null;
100
108
  };
@@ -1 +1 @@
1
- {"version":3,"file":"OauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.195.0",
3
+ "version": "1.196.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -115,7 +115,7 @@
115
115
  "dompurify": "3.4.14",
116
116
  "dotenv": "^17.4.2",
117
117
  "gemoji": "8.1.0",
118
- "googleapis": "^174.0.0",
118
+ "googleapis": "^176.0.0",
119
119
  "ky": "^2.0.2",
120
120
  "marked": "18.0.10",
121
121
  "marked-emoji": "2.0.3",
@@ -67,13 +67,15 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
67
67
  );
68
68
  });
69
69
 
70
- it('reads both tuning numbers from the fleet config file', () => {
70
+ it('reads all tuning numbers from the fleet config file', () => {
71
71
  const fleetConfigFilePath = writeFleetConfig(
72
72
  [
73
73
  'inTmuxLauncherCommand: cl',
74
74
  'liveSessionOauthTokenSelection:',
75
75
  ' maxConcurrentSessionCount: 16',
76
76
  ' fullSpeedFiveHourFreeRatio: 0.4',
77
+ ' minFiveHourFreeRatio: 0.7',
78
+ ' minSevenDayFreeRatio: 0.2',
77
79
  ].join('\n'),
78
80
  );
79
81
 
@@ -82,6 +84,8 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
82
84
  ).toEqual({
83
85
  maxConcurrentSessionCount: 16,
84
86
  fullSpeedFiveHourFreeRatio: 0.4,
87
+ minFiveHourFreeRatio: 0.7,
88
+ minSevenDayFreeRatio: 0.2,
85
89
  });
86
90
  });
87
91
 
@@ -99,6 +103,10 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
99
103
  maxConcurrentSessionCount: 16,
100
104
  fullSpeedFiveHourFreeRatio:
101
105
  DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.fullSpeedFiveHourFreeRatio,
106
+ minFiveHourFreeRatio:
107
+ DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minFiveHourFreeRatio,
108
+ minSevenDayFreeRatio:
109
+ DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minSevenDayFreeRatio,
102
110
  });
103
111
  });
104
112
 
@@ -152,6 +160,30 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
152
160
  ).toThrow('fullSpeedFiveHourFreeRatio');
153
161
  });
154
162
 
163
+ it('throws when the minimum five hour free ratio is above one', () => {
164
+ const fleetConfigFilePath = writeFleetConfig(
165
+ ['liveSessionOauthTokenSelection:', ' minFiveHourFreeRatio: 1.5'].join(
166
+ '\n',
167
+ ),
168
+ );
169
+
170
+ expect(() =>
171
+ loadLiveSessionOauthTokenSelectionSettings(fleetConfigFilePath),
172
+ ).toThrow('minFiveHourFreeRatio');
173
+ });
174
+
175
+ it('throws when the minimum seven day free ratio is above one', () => {
176
+ const fleetConfigFilePath = writeFleetConfig(
177
+ ['liveSessionOauthTokenSelection:', ' minSevenDayFreeRatio: 1.5'].join(
178
+ '\n',
179
+ ),
180
+ );
181
+
182
+ expect(() =>
183
+ loadLiveSessionOauthTokenSelectionSettings(fleetConfigFilePath),
184
+ ).toThrow('minSevenDayFreeRatio');
185
+ });
186
+
155
187
  it('throws when a tuning number is written as a string', () => {
156
188
  const fleetConfigFilePath = writeFleetConfig(
157
189
  [
@@ -105,5 +105,21 @@ export const loadLiveSessionOauthTokenSelectionSettings = (
105
105
  (value) => value > 0 && value <= 1,
106
106
  'above 0 and at most 1',
107
107
  ),
108
+ minFiveHourFreeRatio: readBoundedNumber(
109
+ section,
110
+ 'minFiveHourFreeRatio',
111
+ fleetConfigFilePath,
112
+ DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minFiveHourFreeRatio,
113
+ (value) => value > 0 && value <= 1,
114
+ 'above 0 and at most 1',
115
+ ),
116
+ minSevenDayFreeRatio: readBoundedNumber(
117
+ section,
118
+ 'minSevenDayFreeRatio',
119
+ fleetConfigFilePath,
120
+ DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minSevenDayFreeRatio,
121
+ (value) => value > 0 && value <= 1,
122
+ 'above 0 and at most 1',
123
+ ),
108
124
  };
109
125
  };
@@ -394,6 +394,84 @@ describe('OauthTokenSelectHandler', () => {
394
394
  expect(Number(drawWeightMatch?.[1])).toBeGreaterThan(0);
395
395
  });
396
396
 
397
+ it('excludes a token whose 5h window has only 59% free (CL script threshold is 60%)', () => {
398
+ writeTokenList([{ name: 'low5h', token: 'fake-low5h' }]);
399
+ writeCache('fake-low5h', {
400
+ fiveHourUtilization: 0.41,
401
+ fiveHourReset: NOW + HOUR,
402
+ sevenDayUtilization: 0.1,
403
+ sevenDayReset: NOW + DAY,
404
+ });
405
+
406
+ const handler = new OauthTokenSelectHandler();
407
+ const output = handler.handle({
408
+ tokenListJsonPath: tokenListPath,
409
+ cacheDirectory,
410
+ nowEpochSeconds: NOW,
411
+ });
412
+
413
+ expect(output.selectedToken).toBeNull();
414
+ expect(output.diagnostics.join('\n')).toContain('5h window');
415
+ });
416
+
417
+ it('treats exactly 60% 5h free as eligible in the handler (CL script boundary)', () => {
418
+ writeTokenList([{ name: 'boundary5h', token: 'fake-boundary5h' }]);
419
+ writeCache('fake-boundary5h', {
420
+ fiveHourUtilization: 0.4,
421
+ fiveHourReset: NOW + HOUR,
422
+ sevenDayUtilization: 0.1,
423
+ sevenDayReset: NOW + DAY,
424
+ });
425
+
426
+ const handler = new OauthTokenSelectHandler();
427
+ const output = handler.handle({
428
+ tokenListJsonPath: tokenListPath,
429
+ cacheDirectory,
430
+ nowEpochSeconds: NOW,
431
+ });
432
+
433
+ expect(output.selectedName).toBe('boundary5h');
434
+ });
435
+
436
+ it('excludes a token whose 7d window has only 13% free (CL script threshold is 14%)', () => {
437
+ writeTokenList([{ name: 'low7d', token: 'fake-low7d' }]);
438
+ writeCache('fake-low7d', {
439
+ fiveHourUtilization: 0.1,
440
+ fiveHourReset: NOW + HOUR,
441
+ sevenDayUtilization: 0.87,
442
+ sevenDayReset: NOW + DAY,
443
+ });
444
+
445
+ const handler = new OauthTokenSelectHandler();
446
+ const output = handler.handle({
447
+ tokenListJsonPath: tokenListPath,
448
+ cacheDirectory,
449
+ nowEpochSeconds: NOW,
450
+ });
451
+
452
+ expect(output.selectedToken).toBeNull();
453
+ expect(output.diagnostics.join('\n')).toContain('7d window');
454
+ });
455
+
456
+ it('treats exactly 14% 7d free as eligible in the handler (CL script boundary)', () => {
457
+ writeTokenList([{ name: 'boundary7d', token: 'fake-boundary7d' }]);
458
+ writeCache('fake-boundary7d', {
459
+ fiveHourUtilization: 0.1,
460
+ fiveHourReset: NOW + HOUR,
461
+ sevenDayUtilization: 0.86,
462
+ sevenDayReset: NOW + DAY,
463
+ });
464
+
465
+ const handler = new OauthTokenSelectHandler();
466
+ const output = handler.handle({
467
+ tokenListJsonPath: tokenListPath,
468
+ cacheDirectory,
469
+ nowEpochSeconds: NOW,
470
+ });
471
+
472
+ expect(output.selectedName).toBe('boundary7d');
473
+ });
474
+
397
475
  describe('resolveTokenListJsonPath', () => {
398
476
  it('prefers the explicit path over the environment variable', () => {
399
477
  process.env.CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH = '/from/env.json';
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
2
3
  DEFAULT_SELECTION_WEIGHT,
3
4
  type OauthTokenCandidate,
4
5
  type OauthTokenSelectResult,
@@ -118,6 +119,7 @@ export class OauthTokenSelectHandler {
118
119
  candidates,
119
120
  input.nowEpochSeconds,
120
121
  this.random,
122
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
121
123
  );
122
124
 
123
125
  return {
@@ -145,11 +145,11 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
145
145
  expect(result.selected?.name).toBe('distantResetIdle');
146
146
  });
147
147
 
148
- it('keeps the full concurrent session limit on a nearly used seven day window so the expiring allowance is drained', () => {
148
+ it('excludes a seven day window that has fallen below the minimum free ratio even when sessions are below the concurrent limit', () => {
149
149
  const result = useCase.run(
150
150
  [
151
151
  candidate(
152
- 'soonResetNearlyUsedSevenDay',
152
+ 'nearlyUsedSevenDay',
153
153
  snapshot({
154
154
  sevenDayReset: NOW + 3 * DAY,
155
155
  sevenDayUtilization: 0.9,
@@ -160,29 +160,27 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
160
160
  snapshot({ sevenDayReset: NOW + 6 * DAY }),
161
161
  ),
162
162
  ],
163
- sessionsFor('soonResetNearlyUsedSevenDay', 5),
163
+ sessionsFor('nearlyUsedSevenDay', 5),
164
164
  NOW,
165
165
  SETTINGS,
166
166
  );
167
167
 
168
168
  const nearlyUsed = result.metrics.find(
169
- (m) => m.name === 'soonResetNearlyUsedSevenDay',
169
+ (m) => m.name === 'nearlyUsedSevenDay',
170
170
  );
171
- expect(nearlyUsed?.concurrentSessionLimit).toBe(
172
- MAX_CONCURRENT_SESSION_COUNT,
173
- );
174
- expect(result.selected?.name).toBe('soonResetNearlyUsedSevenDay');
171
+ expect(nearlyUsed?.eligible).toBe(false);
172
+ expect(nearlyUsed?.exclusionReason).toContain('7d window');
173
+ expect(result.selected?.name).toBe('distantResetIdle');
175
174
  });
176
175
 
177
- it('keeps the full concurrent session limit on a seven day window that resets within the hour while its five hour window is still half free', () => {
176
+ it('excludes a token whose seven day window is below the minimum even when it resets within the hour', () => {
178
177
  const result = useCase.run(
179
178
  [
180
179
  candidate(
181
- 'aboutToResetHalfFreeFiveHour',
180
+ 'aboutToResetNearlyUsedSevenDay',
182
181
  snapshot({
183
182
  sevenDayReset: NOW + HOUR,
184
183
  sevenDayUtilization: 0.9,
185
- fiveHourUtilization: 0.5,
186
184
  }),
187
185
  ),
188
186
  candidate(
@@ -190,18 +188,16 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
190
188
  snapshot({ sevenDayReset: NOW + 6 * DAY }),
191
189
  ),
192
190
  ],
193
- sessionsFor('aboutToResetHalfFreeFiveHour', 5),
191
+ sessionsFor('aboutToResetNearlyUsedSevenDay', 5),
194
192
  NOW,
195
193
  SETTINGS,
196
194
  );
197
195
 
198
196
  const aboutToReset = result.metrics.find(
199
- (m) => m.name === 'aboutToResetHalfFreeFiveHour',
197
+ (m) => m.name === 'aboutToResetNearlyUsedSevenDay',
200
198
  );
201
- expect(aboutToReset?.concurrentSessionLimit).toBe(
202
- MAX_CONCURRENT_SESSION_COUNT,
203
- );
204
- expect(result.selected?.name).toBe('aboutToResetHalfFreeFiveHour');
199
+ expect(aboutToReset?.eligible).toBe(false);
200
+ expect(result.selected?.name).toBe('distantResetIdle');
205
201
  });
206
202
 
207
203
  it('still throttles a seven day window that resets within the hour once its five hour window falls below half free', () => {
@@ -509,6 +505,130 @@ describe('LiveSessionOauthTokenCandidateMetrics selectionWeight', () => {
509
505
  });
510
506
  });
511
507
 
508
+ describe('LiveSessionOauthTokenSelectUseCase minimum free ratio thresholds', () => {
509
+ const useCase = new LiveSessionOauthTokenSelectUseCase();
510
+
511
+ it('excludes a token whose five hour window has less than the minimum free ratio', () => {
512
+ const result = useCase.run(
513
+ [
514
+ candidate('narrowFiveHourMin', snapshot({ fiveHourUtilization: 0.5 })),
515
+ candidate('freeFiveHourMin', snapshot({})),
516
+ ],
517
+ [],
518
+ NOW,
519
+ SETTINGS,
520
+ );
521
+
522
+ const narrow = result.metrics.find((m) => m.name === 'narrowFiveHourMin');
523
+ expect(narrow?.eligible).toBe(false);
524
+ expect(narrow?.exclusionReason).toContain('5h window');
525
+ expect(result.selected?.name).toBe('freeFiveHourMin');
526
+ });
527
+
528
+ it('excludes a token whose seven day window has less than the minimum free ratio', () => {
529
+ const result = useCase.run(
530
+ [
531
+ candidate(
532
+ 'nearlyUsedSevenDayMin',
533
+ snapshot({ sevenDayUtilization: 0.9 }),
534
+ ),
535
+ candidate('freeSevenDayMin', snapshot({})),
536
+ ],
537
+ [],
538
+ NOW,
539
+ SETTINGS,
540
+ );
541
+
542
+ const nearlyUsed = result.metrics.find(
543
+ (m) => m.name === 'nearlyUsedSevenDayMin',
544
+ );
545
+ expect(nearlyUsed?.eligible).toBe(false);
546
+ expect(nearlyUsed?.exclusionReason).toContain('7d window');
547
+ expect(result.selected?.name).toBe('freeSevenDayMin');
548
+ });
549
+
550
+ it('selects a token whose ratios are at exactly the minimum free ratio thresholds', () => {
551
+ const result = useCase.run(
552
+ [
553
+ candidate(
554
+ 'atThreshold',
555
+ snapshot({ fiveHourUtilization: 0.4, sevenDayUtilization: 0.86 }),
556
+ ),
557
+ ],
558
+ [],
559
+ NOW,
560
+ SETTINGS,
561
+ );
562
+
563
+ const atThreshold = result.metrics.find((m) => m.name === 'atThreshold');
564
+ expect(atThreshold?.eligible).toBe(true);
565
+ expect(result.selected?.name).toBe('atThreshold');
566
+ });
567
+
568
+ it('honours a fleet supplied minimum five hour free ratio', () => {
569
+ const result = useCase.run(
570
+ [
571
+ candidate(
572
+ 'narrowForFleetFiveHour',
573
+ snapshot({ fiveHourUtilization: 0.15 }),
574
+ ),
575
+ candidate('freeForFleetFiveHour', snapshot({})),
576
+ ],
577
+ [],
578
+ NOW,
579
+ settingsWith({ minFiveHourFreeRatio: 0.9 }),
580
+ );
581
+
582
+ const narrow = result.metrics.find(
583
+ (m) => m.name === 'narrowForFleetFiveHour',
584
+ );
585
+ expect(narrow?.eligible).toBe(false);
586
+ expect(narrow?.exclusionReason).toContain('5h window');
587
+ expect(result.selected?.name).toBe('freeForFleetFiveHour');
588
+ });
589
+
590
+ it('honours a fleet supplied minimum seven day free ratio', () => {
591
+ const result = useCase.run(
592
+ [
593
+ candidate(
594
+ 'narrowForFleetSevenDay',
595
+ snapshot({ sevenDayUtilization: 0.5 }),
596
+ ),
597
+ candidate('freeForFleetSevenDay', snapshot({})),
598
+ ],
599
+ [],
600
+ NOW,
601
+ settingsWith({ minSevenDayFreeRatio: 0.6 }),
602
+ );
603
+
604
+ const narrow = result.metrics.find(
605
+ (m) => m.name === 'narrowForFleetSevenDay',
606
+ );
607
+ expect(narrow?.eligible).toBe(false);
608
+ expect(narrow?.exclusionReason).toContain('7d window');
609
+ expect(result.selected?.name).toBe('freeForFleetSevenDay');
610
+ });
611
+
612
+ it('reports the five hour window exclusion reason when both windows are below their minimums', () => {
613
+ const result = useCase.run(
614
+ [
615
+ candidate(
616
+ 'bothNarrow',
617
+ snapshot({ fiveHourUtilization: 0.5, sevenDayUtilization: 0.9 }),
618
+ ),
619
+ ],
620
+ [],
621
+ NOW,
622
+ SETTINGS,
623
+ );
624
+
625
+ const bothNarrow = result.metrics.find((m) => m.name === 'bothNarrow');
626
+ expect(bothNarrow?.eligible).toBe(false);
627
+ expect(bothNarrow?.exclusionReason).toContain('5h window');
628
+ expect(bothNarrow?.exclusionReason).not.toContain('7d window');
629
+ });
630
+ });
631
+
512
632
  describe('liveSessionConcurrentLimitOf', () => {
513
633
  it('gives the full limit while the five hour window is at or above the full speed free ratio', () => {
514
634
  expect(liveSessionConcurrentLimitOf(1, 1, SETTINGS)).toBe(
@@ -8,12 +8,16 @@ import {
8
8
  export type LiveSessionOauthTokenSelectionSettings = {
9
9
  maxConcurrentSessionCount: number;
10
10
  fullSpeedFiveHourFreeRatio: number;
11
+ minFiveHourFreeRatio: number;
12
+ minSevenDayFreeRatio: number;
11
13
  };
12
14
 
13
15
  export const DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS: LiveSessionOauthTokenSelectionSettings =
14
16
  {
15
17
  maxConcurrentSessionCount: 10,
16
18
  fullSpeedFiveHourFreeRatio: 0.5,
19
+ minFiveHourFreeRatio: 0.6,
20
+ minSevenDayFreeRatio: 0.14,
17
21
  };
18
22
 
19
23
  export const liveSessionConcurrentLimitOf = (
@@ -80,6 +84,12 @@ export class LiveSessionOauthTokenSelectUseCase {
80
84
  selectionWeightOf(candidate),
81
85
  settings,
82
86
  );
87
+ const exclusionReason = this.liveSessionExclusionReason(
88
+ rateLimitMetric.exclusionReason,
89
+ rateLimitMetric.fiveHourFreeRatio,
90
+ rateLimitMetric.sevenDayFreeRatio,
91
+ settings,
92
+ );
83
93
  return {
84
94
  candidate,
85
95
  metric: {
@@ -90,8 +100,8 @@ export class LiveSessionOauthTokenSelectUseCase {
90
100
  liveSessionCount,
91
101
  concurrentSessionLimit,
92
102
  hasConcurrencyHeadroom: liveSessionCount < concurrentSessionLimit,
93
- eligible: rateLimitMetric.eligible,
94
- exclusionReason: rateLimitMetric.exclusionReason,
103
+ eligible: exclusionReason === null,
104
+ exclusionReason,
95
105
  selectionWeight: selectionWeightOf(candidate),
96
106
  },
97
107
  };
@@ -113,6 +123,24 @@ export class LiveSessionOauthTokenSelectUseCase {
113
123
  return { selected: selected.candidate, metrics };
114
124
  };
115
125
 
126
+ private liveSessionExclusionReason = (
127
+ rateLimitExclusionReason: string | null,
128
+ fiveHourFreeRatio: number,
129
+ sevenDayFreeRatio: number,
130
+ settings: LiveSessionOauthTokenSelectionSettings,
131
+ ): string | null => {
132
+ if (rateLimitExclusionReason !== null) {
133
+ return rateLimitExclusionReason;
134
+ }
135
+ if (fiveHourFreeRatio < settings.minFiveHourFreeRatio) {
136
+ return `5h window only ${Math.round(fiveHourFreeRatio * 100)}% free (requires >= ${Math.round(settings.minFiveHourFreeRatio * 100)}% for live session selection)`;
137
+ }
138
+ if (sevenDayFreeRatio < settings.minSevenDayFreeRatio) {
139
+ return `7d window only ${Math.round(sevenDayFreeRatio * 100)}% free (requires >= ${Math.round(settings.minSevenDayFreeRatio * 100)}% for live session selection)`;
140
+ }
141
+ return null;
142
+ };
143
+
116
144
  private preferred = (
117
145
  candidateMetric: LiveSessionOauthTokenCandidateMetrics,
118
146
  incumbentMetric: LiveSessionOauthTokenCandidateMetrics,
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
2
3
  OauthTokenCandidate,
3
4
  OauthTokenSelectUseCase,
4
5
  OauthTokenWindowSnapshot,
@@ -550,3 +551,101 @@ describe('sevenDayUrgencyFactor', () => {
550
551
  expect(almostReset).toBe(SEVEN_DAY_WINDOW_HOURS);
551
552
  });
552
553
  });
554
+
555
+ describe('OauthTokenSelectUseCase with CL script thresholds', () => {
556
+ const useCase = new OauthTokenSelectUseCase();
557
+
558
+ it('excludes a token whose 5h window is less than 60% free when using CL script thresholds', () => {
559
+ const result = useCase.run(
560
+ [
561
+ candidate('busy5h', snapshot({ fiveHourUtilization: 0.41 })),
562
+ candidate('ok', snapshot({ fiveHourUtilization: 0.4 })),
563
+ ],
564
+ NOW,
565
+ Math.random,
566
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
567
+ );
568
+
569
+ expect(result.selected?.name).toBe('ok');
570
+ const busy = result.metrics.find((m) => m.name === 'busy5h');
571
+ expect(busy?.eligible).toBe(false);
572
+ expect(busy?.exclusionReason).toContain('5h window');
573
+ });
574
+
575
+ it('treats exactly 40% used 5h utilization as eligible under CL script thresholds (boundary)', () => {
576
+ const result = useCase.run(
577
+ [candidate('boundary', snapshot({ fiveHourUtilization: 0.4 }))],
578
+ NOW,
579
+ Math.random,
580
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
581
+ );
582
+
583
+ expect(result.selected?.name).toBe('boundary');
584
+ const boundary = result.metrics.find((m) => m.name === 'boundary');
585
+ expect(boundary?.fiveHourFreeRatio).toBeCloseTo(0.6, 9);
586
+ });
587
+
588
+ it('excludes a token whose 7d window is less than 14% free when using CL script thresholds', () => {
589
+ const result = useCase.run(
590
+ [
591
+ candidate('busy7d', snapshot({ sevenDayUtilization: 0.87 })),
592
+ candidate('ok', snapshot({ sevenDayUtilization: 0.86 })),
593
+ ],
594
+ NOW,
595
+ Math.random,
596
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
597
+ );
598
+
599
+ expect(result.selected?.name).toBe('ok');
600
+ const busy = result.metrics.find((m) => m.name === 'busy7d');
601
+ expect(busy?.eligible).toBe(false);
602
+ expect(busy?.exclusionReason).toContain('7d window');
603
+ });
604
+
605
+ it('treats exactly 86% used 7d utilization as eligible under CL script thresholds (boundary)', () => {
606
+ const result = useCase.run(
607
+ [candidate('boundary', snapshot({ sevenDayUtilization: 0.86 }))],
608
+ NOW,
609
+ Math.random,
610
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
611
+ );
612
+
613
+ expect(result.selected?.name).toBe('boundary');
614
+ const boundary = result.metrics.find((m) => m.name === 'boundary');
615
+ expect(boundary?.sevenDayFreeRatio).toBeCloseTo(0.14, 9);
616
+ });
617
+
618
+ it('returns null when only a token with 59% 5h free is available under CL script thresholds', () => {
619
+ const result = useCase.run(
620
+ [candidate('marginal', snapshot({ fiveHourUtilization: 0.41 }))],
621
+ NOW,
622
+ Math.random,
623
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
624
+ );
625
+
626
+ expect(result.selected).toBeNull();
627
+ });
628
+
629
+ it('returns null when only a token with 13% 7d free is available under CL script thresholds', () => {
630
+ const result = useCase.run(
631
+ [candidate('marginal', snapshot({ sevenDayUtilization: 0.87 }))],
632
+ NOW,
633
+ Math.random,
634
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
635
+ );
636
+
637
+ expect(result.selected).toBeNull();
638
+ });
639
+
640
+ it('includes the correct threshold percentages in the exclusion reason message', () => {
641
+ const result = useCase.run(
642
+ [candidate('low5h', snapshot({ fiveHourUtilization: 0.5 }))],
643
+ NOW,
644
+ Math.random,
645
+ CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
646
+ );
647
+
648
+ const low5h = result.metrics.find((m) => m.name === 'low5h');
649
+ expect(low5h?.exclusionReason).toContain('60%');
650
+ });
651
+ });
@@ -39,12 +39,29 @@ export type OauthTokenSelectResult = {
39
39
  metrics: OauthTokenCandidateMetrics[];
40
40
  };
41
41
 
42
+ export type OauthTokenSelectionThresholds = {
43
+ fiveHourMinFreeRatio: number;
44
+ sevenDayMinFreeRatio: number;
45
+ };
46
+
42
47
  const SECONDS_PER_DAY = 86400;
43
48
  const SEVEN_DAYS_IN_SECONDS = 7 * SECONDS_PER_DAY;
44
49
 
45
50
  export const FIVE_HOUR_MIN_FREE_RATIO = 0.25;
46
51
  export const SEVEN_DAY_MIN_FREE_RATIO = 0.03;
47
52
 
53
+ export const DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds =
54
+ {
55
+ fiveHourMinFreeRatio: FIVE_HOUR_MIN_FREE_RATIO,
56
+ sevenDayMinFreeRatio: SEVEN_DAY_MIN_FREE_RATIO,
57
+ };
58
+
59
+ export const CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds =
60
+ {
61
+ fiveHourMinFreeRatio: 0.6,
62
+ sevenDayMinFreeRatio: 0.14,
63
+ };
64
+
48
65
  export const SEVEN_DAY_WINDOW_HOURS = 168;
49
66
  export const MIN_HOURS_TO_RESET = 1;
50
67
  const SECONDS_PER_HOUR = 3600;
@@ -91,9 +108,14 @@ export class OauthTokenSelectUseCase {
91
108
  candidates: OauthTokenCandidate[],
92
109
  nowEpochSeconds: number,
93
110
  random: SelectionRandom = Math.random,
111
+ thresholds: OauthTokenSelectionThresholds = DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
94
112
  ): OauthTokenSelectResult => {
95
113
  const evaluated = candidates.map((candidate) => {
96
- const evaluatedMetric = this.evaluate(candidate, nowEpochSeconds);
114
+ const evaluatedMetric = this.evaluate(
115
+ candidate,
116
+ nowEpochSeconds,
117
+ thresholds,
118
+ );
97
119
  return {
98
120
  candidate,
99
121
  metric: {
@@ -136,6 +158,7 @@ export class OauthTokenSelectUseCase {
136
158
  private evaluate = (
137
159
  candidate: OauthTokenCandidate,
138
160
  nowEpochSeconds: number,
161
+ thresholds: OauthTokenSelectionThresholds,
139
162
  ): Omit<OauthTokenCandidateMetrics, 'drawWeight'> => {
140
163
  const fiveHourFreeRatio = this.fiveHourFreeRatio(
141
164
  candidate.snapshot,
@@ -156,6 +179,7 @@ export class OauthTokenSelectUseCase {
156
179
  candidate.fableRejected,
157
180
  fiveHourFreeRatio,
158
181
  sevenDayFreeRatio,
182
+ thresholds,
159
183
  );
160
184
 
161
185
  return {
@@ -174,6 +198,7 @@ export class OauthTokenSelectUseCase {
174
198
  fableRejected: boolean,
175
199
  fiveHourFreeRatio: number,
176
200
  sevenDayFreeRatio: number,
201
+ thresholds: OauthTokenSelectionThresholds,
177
202
  ): string | null => {
178
203
  if (subscriptionDisabled) {
179
204
  return 'organization has disabled Claude subscription access for Claude Code';
@@ -184,11 +209,11 @@ export class OauthTokenSelectUseCase {
184
209
  if (fableRejected) {
185
210
  return 'fable weekly limit exhausted (a fable request was rejected with HTTP 429)';
186
211
  }
187
- if (fiveHourFreeRatio < FIVE_HOUR_MIN_FREE_RATIO) {
188
- return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(FIVE_HOUR_MIN_FREE_RATIO)}%)`;
212
+ if (fiveHourFreeRatio < thresholds.fiveHourMinFreeRatio) {
213
+ return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(thresholds.fiveHourMinFreeRatio)}%)`;
189
214
  }
190
- if (sevenDayFreeRatio < SEVEN_DAY_MIN_FREE_RATIO) {
191
- return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(SEVEN_DAY_MIN_FREE_RATIO)}%)`;
215
+ if (sevenDayFreeRatio < thresholds.sevenDayMinFreeRatio) {
216
+ return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(thresholds.sevenDayMinFreeRatio)}%)`;
192
217
  }
193
218
  return null;
194
219
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fleetConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,sCAAsC,EACvC,MAAM,6DAA6D,CAAC;AAErE,eAAO,MAAM,2CAA2C,sBAAsB,CAAC;AAE/E,eAAO,MAAM,8CAA8C,mCACzB,CAAC;AAEnC,eAAO,MAAM,0BAA0B,GACrC,UAAU,MAAM,GAAG,IAAI,KACtB,MAAM,GAAG,IAUX,CAAC;AAwDF,eAAO,MAAM,0CAA0C,GACrD,qBAAqB,MAAM,GAAG,IAAI,KACjC,sCA0BF,CAAC"}
1
+ {"version":3,"file":"fleetConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,sCAAsC,EACvC,MAAM,6DAA6D,CAAC;AAErE,eAAO,MAAM,2CAA2C,sBAAsB,CAAC;AAE/E,eAAO,MAAM,8CAA8C,mCACzB,CAAC;AAEnC,eAAO,MAAM,0BAA0B,GACrC,UAAU,MAAM,GAAG,IAAI,KACtB,MAAM,GAAG,IAUX,CAAC;AAwDF,eAAO,MAAM,0CAA0C,GACrD,qBAAqB,MAAM,GAAG,IAAI,KACjC,sCA0CF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,uBAAuB,EACvB,KAAK,eAAe,EACrB,MAAM,kDAAkD,CAAC;AAQ1D,MAAM,MAAM,4BAA4B,GAAG;IACzC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,GAAG,IAAI,KAC1B,MAAM,GAAG,IASX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,GAAG,IAAI,KAC/B,MASF,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,GAAE,uBAAuD,EAChE,MAAM,GAAE,eAA6B;IAGxD,MAAM,GACJ,OAAO,4BAA4B,KAClC,6BAA6B,CAgE9B;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;CACH"}
1
+ {"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,uBAAuB,EACvB,KAAK,eAAe,EACrB,MAAM,kDAAkD,CAAC;AAQ1D,MAAM,MAAM,4BAA4B,GAAG;IACzC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,GAAG,IAAI,KAC1B,MAAM,GAAG,IASX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,GAAG,IAAI,KAC/B,MASF,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,GAAE,uBAAuD,EAChE,MAAM,GAAE,eAA6B;IAGxD,MAAM,GACJ,OAAO,4BAA4B,KAClC,6BAA6B,CAiE9B;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;CACH"}
@@ -3,6 +3,8 @@ import { type OauthTokenCandidate, OauthTokenSelectUseCase } from './OauthTokenS
3
3
  export type LiveSessionOauthTokenSelectionSettings = {
4
4
  maxConcurrentSessionCount: number;
5
5
  fullSpeedFiveHourFreeRatio: number;
6
+ minFiveHourFreeRatio: number;
7
+ minSevenDayFreeRatio: number;
6
8
  };
7
9
  export declare const DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS: LiveSessionOauthTokenSelectionSettings;
8
10
  export declare const liveSessionConcurrentLimitOf: (fiveHourFreeRatio: number, selectionWeight: number, settings: LiveSessionOauthTokenSelectionSettings) => number;
@@ -26,6 +28,7 @@ export declare class LiveSessionOauthTokenSelectUseCase {
26
28
  private readonly rateLimitSelectUseCase;
27
29
  constructor(rateLimitSelectUseCase?: OauthTokenSelectUseCase);
28
30
  run: (candidates: OauthTokenCandidate[], liveSessions: ClaudeLiveSession[], nowEpochSeconds: number, settings: LiveSessionOauthTokenSelectionSettings) => LiveSessionOauthTokenSelectResult;
31
+ private liveSessionExclusionReason;
29
32
  private preferred;
30
33
  private liveSessionCountByToken;
31
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAC1F,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EAExB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,sCAAsC,GAAG;IACnD,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,eAAO,MAAM,mDAAmD,EAAE,sCAI/D,CAAC;AAEJ,eAAO,MAAM,4BAA4B,GACvC,mBAAmB,MAAM,EACzB,iBAAiB,MAAM,EACvB,UAAU,sCAAsC,KAC/C,MAaF,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,OAAO,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,qCAAqC,EAAE,CAAC;CAClD,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBAAtB,sBAAsB,GAAE,uBAAuD;IAGlG,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,cAAc,iBAAiB,EAAE,EACjC,iBAAiB,MAAM,EACvB,UAAU,sCAAsC,KAC/C,iCAAiC,CAgDlC;IAEF,OAAO,CAAC,SAAS,CAgBf;IAEF,OAAO,CAAC,uBAAuB,CAe7B;CACH"}
1
+ {"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAC1F,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EAExB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,sCAAsC,GAAG;IACnD,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B,EAAE,MAAM,CAAC;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,mDAAmD,EAAE,sCAM/D,CAAC;AAEJ,eAAO,MAAM,4BAA4B,GACvC,mBAAmB,MAAM,EACzB,iBAAiB,MAAM,EACvB,UAAU,sCAAsC,KAC/C,MAaF,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,OAAO,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,qCAAqC,EAAE,CAAC;CAClD,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBAAtB,sBAAsB,GAAE,uBAAuD;IAGlG,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,cAAc,iBAAiB,EAAE,EACjC,iBAAiB,MAAM,EACvB,UAAU,sCAAsC,KAC/C,iCAAiC,CAsDlC;IAEF,OAAO,CAAC,0BAA0B,CAgBhC;IAEF,OAAO,CAAC,SAAS,CAgBf;IAEF,OAAO,CAAC,uBAAuB,CAe7B;CACH"}
@@ -29,14 +29,20 @@ export type OauthTokenSelectResult = {
29
29
  selected: OauthTokenCandidate | null;
30
30
  metrics: OauthTokenCandidateMetrics[];
31
31
  };
32
+ export type OauthTokenSelectionThresholds = {
33
+ fiveHourMinFreeRatio: number;
34
+ sevenDayMinFreeRatio: number;
35
+ };
32
36
  export declare const FIVE_HOUR_MIN_FREE_RATIO = 0.25;
33
37
  export declare const SEVEN_DAY_MIN_FREE_RATIO = 0.03;
38
+ export declare const DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds;
39
+ export declare const CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds;
34
40
  export declare const SEVEN_DAY_WINDOW_HOURS = 168;
35
41
  export declare const MIN_HOURS_TO_RESET = 1;
36
42
  export declare const sevenDayUrgencyFactor: (sevenDayFreeRatio: number, sevenDayEndEpoch: number, nowEpochSeconds: number) => number;
37
43
  export declare const selectWeightedCandidate: <Entry>(eligible: Entry[], weightOf: (entry: Entry) => number, deterministicBest: Entry, random: SelectionRandom) => Entry;
38
44
  export declare class OauthTokenSelectUseCase {
39
- run: (candidates: OauthTokenCandidate[], nowEpochSeconds: number, random?: SelectionRandom) => OauthTokenSelectResult;
45
+ run: (candidates: OauthTokenCandidate[], nowEpochSeconds: number, random?: SelectionRandom, thresholds?: OauthTokenSelectionThresholds) => OauthTokenSelectResult;
40
46
  private evaluate;
41
47
  private exclusionReason;
42
48
  private fiveHourFreeRatio;
@@ -1 +1 @@
1
- {"version":3,"file":"OauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC;AAE3C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,iBAAiB,GAAI,WAAW,mBAAmB,KAAG,MAGlE,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC,CAAC;AAKF,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAGpC,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,EACzB,kBAAkB,MAAM,EACxB,iBAAiB,MAAM,KACtB,MAMF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAC3C,UAAU,KAAK,EAAE,EACjB,UAAU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,EAClC,mBAAmB,KAAK,EACxB,QAAQ,eAAe,KACtB,KAkBF,CAAC;AAEF,qBAAa,uBAAuB;IAClC,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,KACpC,sBAAsB,CAwCvB;IAEF,OAAO,CAAC,QAAQ,CAiCd;IAEF,OAAO,CAAC,eAAe,CAuBrB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,gBAAgB,CActB;IAEF,OAAO,CAAC,aAAa,CAGwC;IAE7D,OAAO,CAAC,wBAAwB,CAG9B;IAEF,OAAO,CAAC,SAAS,CAAsD;CACxE"}
1
+ {"version":3,"file":"OauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC;AAE3C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,iBAAiB,GAAI,WAAW,mBAAmB,KAAG,MAGlE,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAKF,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,wCAAwC,EAAE,6BAIpD,CAAC;AAEJ,eAAO,MAAM,0CAA0C,EAAE,6BAItD,CAAC;AAEJ,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAGpC,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,EACzB,kBAAkB,MAAM,EACxB,iBAAiB,MAAM,KACtB,MAMF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAC3C,UAAU,KAAK,EAAE,EACjB,UAAU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,EAClC,mBAAmB,KAAK,EACxB,QAAQ,eAAe,KACtB,KAkBF,CAAC;AAEF,qBAAa,uBAAuB;IAClC,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,EACrC,aAAY,6BAAwE,KACnF,sBAAsB,CA4CvB;IAEF,OAAO,CAAC,QAAQ,CAmCd;IAEF,OAAO,CAAC,eAAe,CAwBrB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,gBAAgB,CActB;IAEF,OAAO,CAAC,aAAa,CAGwC;IAE7D,OAAO,CAAC,wBAAwB,CAG9B;IAEF,OAAO,CAAC,SAAS,CAAsD;CACxE"}