github-issue-tower-defence-management 1.65.0 → 1.67.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 (32) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +7 -7
  3. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +1 -1
  4. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  5. package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js +5 -0
  6. package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js.map +1 -1
  7. package/bin/domain/entities/WorkflowStatus.js +2 -6
  8. package/bin/domain/entities/WorkflowStatus.js.map +1 -1
  9. package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js +14 -1
  10. package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js.map +1 -1
  11. package/bin/domain/usecases/StartPreparationUseCase.js +59 -121
  12. package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
  13. package/package.json +1 -1
  14. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +1 -0
  15. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +14 -0
  16. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +5 -0
  17. package/src/domain/entities/ClaudeTokenUsage.ts +1 -0
  18. package/src/domain/entities/WorkflowStatus.ts +2 -5
  19. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +432 -36
  20. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +31 -0
  21. package/src/domain/usecases/StartPreparationUseCase.test.ts +87 -306
  22. package/src/domain/usecases/StartPreparationUseCase.ts +80 -175
  23. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  24. package/types/adapter/repositories/ProxyClaudeTokenUsageRepository.d.ts.map +1 -1
  25. package/types/domain/entities/ClaudeTokenUsage.d.ts +1 -0
  26. package/types/domain/entities/ClaudeTokenUsage.d.ts.map +1 -1
  27. package/types/domain/entities/WorkflowStatus.d.ts +1 -1
  28. package/types/domain/entities/WorkflowStatus.d.ts.map +1 -1
  29. package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts +3 -1
  30. package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts.map +1 -1
  31. package/types/domain/usecases/StartPreparationUseCase.d.ts +2 -6
  32. package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
@@ -8,16 +8,8 @@ import {
8
8
  PREPARATION_STATUS_NAME,
9
9
  } from '../entities/WorkflowStatus';
10
10
 
11
- const MAXIMUM_PREPARING_PROCESS_COUNT_PER_TOKEN = 6;
12
- const PROCESS_COUNT_DECAY_START_UTILIZATION = 0.8;
13
- const PROCESS_COUNT_ZERO_UTILIZATION = 0.95;
14
- const PROCESS_COUNT_DECAY_STEEPNESS = 2;
15
-
16
- type RotationToken = {
17
- token: string;
18
- model: string;
19
- maximumPreparingProcessCount: number;
20
- };
11
+ const SEVEN_DAY_NORMAL_CONCURRENT_LIMIT = 6;
12
+ const SEVEN_DAY_THROTTLE_START_THRESHOLD = 0.8;
21
13
 
22
14
  export type RotationOrderEntry = {
23
15
  name: string;
@@ -44,11 +36,6 @@ export class StartPreparationUseCase {
44
36
  private readonly claudeTokenUsageRepository: ClaudeTokenUsageRepository,
45
37
  ) {}
46
38
 
47
- private readonly KNOWN_MODEL_SPECIFIC_LIMIT_TYPES = [
48
- 'seven_day_sonnet',
49
- 'seven_day_opus',
50
- ];
51
-
52
39
  private weeklyLimitTypeForModel = (modelName: string | null): string => {
53
40
  const normalized = (modelName ?? '').toLowerCase();
54
41
  if (normalized.includes('sonnet')) return 'seven_day_sonnet';
@@ -56,137 +43,70 @@ export class StartPreparationUseCase {
56
43
  return 'seven_day';
57
44
  };
58
45
 
59
- private deriveFallbackModelName = (
60
- limitType: string,
61
- defaultModelName: string,
62
- ): string => {
63
- if (limitType === this.weeklyLimitTypeForModel(defaultModelName)) {
64
- return defaultModelName;
65
- }
66
- if (
67
- limitType === 'seven_day_opus' &&
68
- defaultModelName.toLowerCase().includes('sonnet')
69
- ) {
70
- return defaultModelName.replace(/sonnet/gi, 'opus');
71
- }
72
- if (
73
- limitType === 'seven_day_sonnet' &&
74
- defaultModelName.toLowerCase().includes('opus')
75
- ) {
76
- return defaultModelName.replace(/opus/gi, 'sonnet');
77
- }
78
- return defaultModelName;
79
- };
80
-
81
- private selectModelForToken = (
46
+ private isModelWeeklyLimitRejected = (
82
47
  usage: ClaudeTokenUsage,
83
- defaultModelName: string | null,
84
- ): string | null => {
85
- const generalLimit = usage.modelWeeklyLimits['seven_day'];
86
- if (generalLimit !== undefined && generalLimit.rejected) return null;
87
-
88
- if (defaultModelName === null) return null;
89
-
90
- const defaultLimitType = this.weeklyLimitTypeForModel(defaultModelName);
91
- const candidateLimitTypes = [
92
- defaultLimitType,
93
- ...this.KNOWN_MODEL_SPECIFIC_LIMIT_TYPES.filter(
94
- (t) => t !== defaultLimitType,
95
- ),
96
- ];
97
-
98
- for (const limitType of candidateLimitTypes) {
99
- const limit = usage.modelWeeklyLimits[limitType];
100
- if (limit === undefined || !limit.rejected) {
101
- return this.deriveFallbackModelName(limitType, defaultModelName);
102
- }
103
- }
104
-
105
- return null;
48
+ weeklyLimitType: string,
49
+ ): boolean => {
50
+ const specific = usage.modelWeeklyLimits[weeklyLimitType];
51
+ if (specific !== undefined && specific.rejected) return true;
52
+ const general = usage.modelWeeklyLimits['seven_day'];
53
+ return general !== undefined && general.rejected;
106
54
  };
107
55
 
108
- private maximumPreparingProcessCountForToken = (
109
- fiveHourUtilization: number,
110
- ): number => {
111
- if (fiveHourUtilization <= PROCESS_COUNT_DECAY_START_UTILIZATION) {
112
- return MAXIMUM_PREPARING_PROCESS_COUNT_PER_TOKEN;
56
+ private getTokenConcurrentLimit = (sevenDayUtilization: number): number => {
57
+ if (sevenDayUtilization < SEVEN_DAY_THROTTLE_START_THRESHOLD) {
58
+ return SEVEN_DAY_NORMAL_CONCURRENT_LIMIT;
113
59
  }
114
- if (fiveHourUtilization >= PROCESS_COUNT_ZERO_UTILIZATION) {
115
- return 0;
116
- }
117
- const decayProgress =
118
- (fiveHourUtilization - PROCESS_COUNT_DECAY_START_UTILIZATION) /
119
- (PROCESS_COUNT_ZERO_UTILIZATION - PROCESS_COUNT_DECAY_START_UTILIZATION);
120
- const minimumMultiplier = Math.exp(-PROCESS_COUNT_DECAY_STEEPNESS);
121
- const remainingRatio =
122
- (Math.exp(-PROCESS_COUNT_DECAY_STEEPNESS * decayProgress) -
123
- minimumMultiplier) /
124
- (1 - minimumMultiplier);
60
+ const remaining =
61
+ (1 - sevenDayUtilization) / (1 - SEVEN_DAY_THROTTLE_START_THRESHOLD);
125
62
  return Math.max(
126
63
  1,
127
- Math.floor(MAXIMUM_PREPARING_PROCESS_COUNT_PER_TOKEN * remainingRatio),
64
+ Math.ceil(SEVEN_DAY_NORMAL_CONCURRENT_LIMIT * remaining),
128
65
  );
129
66
  };
130
67
 
131
68
  private selectRotationTokens = (
132
69
  tokenUsages: ClaudeTokenUsage[],
70
+ utilizationPercentageThreshold: number,
133
71
  modelName: string | null,
134
- ): RotationToken[] => {
135
- return tokenUsages
72
+ maxConcurrent: number,
73
+ ): { tokens: string[]; effectiveCap: number } => {
74
+ const weeklyLimitType = this.weeklyLimitTypeForModel(modelName);
75
+ const eligibleTokens = tokenUsages
136
76
  .filter((usage) => !usage.blocked)
137
77
  .filter((usage) => !usage.rejected)
138
- .flatMap((usage) => {
139
- const selectedModel = this.selectModelForToken(usage, modelName);
140
- if (selectedModel === null) return [];
141
- return [
142
- {
143
- token: usage.token,
144
- model: selectedModel,
145
- fiveHourUtilization: usage.fiveHourUtilization,
146
- maximumPreparingProcessCount:
147
- this.maximumPreparingProcessCountForToken(
148
- usage.fiveHourUtilization,
149
- ),
150
- },
151
- ];
152
- })
153
- .filter((usage) => usage.maximumPreparingProcessCount > 0)
154
- .sort((a, b) => a.fiveHourUtilization - b.fiveHourUtilization)
155
- .map((usage) => ({
156
- token: usage.token,
157
- model: usage.model,
158
- maximumPreparingProcessCount: usage.maximumPreparingProcessCount,
159
- }));
160
- };
78
+ .filter(
79
+ (usage) => !this.isModelWeeklyLimitRejected(usage, weeklyLimitType),
80
+ )
81
+ .filter(
82
+ (usage) =>
83
+ usage.fiveHourUtilization * 100 < utilizationPercentageThreshold,
84
+ )
85
+ .sort((a, b) => a.sevenDayUtilization - b.sevenDayUtilization);
161
86
 
162
- private createRotationTokenSlots = (
163
- rotationTokens: RotationToken[],
164
- ): Array<{ token: string; model: string }> => {
165
- const slotCount = Math.max(
166
- ...rotationTokens.map((token) => token.maximumPreparingProcessCount),
167
- );
168
- const tokenSlots: Array<{ token: string; model: string }> = [];
169
- for (let i = 0; i < slotCount; i++) {
170
- tokenSlots.push(
171
- ...rotationTokens
172
- .filter((token) => i < token.maximumPreparingProcessCount)
173
- .map((token) => ({ token: token.token, model: token.model })),
174
- );
87
+ if (eligibleTokens.length === 0) {
88
+ return { tokens: [], effectiveCap: 0 };
175
89
  }
176
- return tokenSlots;
177
- };
178
90
 
179
- private resolveMaximumPreparingIssuesCount = (
180
- configuredMaximumPreparingIssuesCount: number | null,
181
- rotationTokenSlots: Array<{ token: string; model: string }> | null,
182
- ): number => {
183
- if (rotationTokenSlots === null) {
184
- return configuredMaximumPreparingIssuesCount ?? 6;
91
+ const tokensWithLimits = eligibleTokens.map((usage) => ({
92
+ token: usage.token,
93
+ limit: this.getTokenConcurrentLimit(usage.sevenDayUtilization),
94
+ }));
95
+
96
+ const totalCapacity = tokensWithLimits.reduce((sum, t) => sum + t.limit, 0);
97
+ const effectiveCap = Math.min(maxConcurrent, totalCapacity);
98
+
99
+ const maxLimit = Math.max(...tokensWithLimits.map((t) => t.limit));
100
+ const rotationList: string[] = [];
101
+ for (let round = 0; round < maxLimit; round++) {
102
+ for (const t of tokensWithLimits) {
103
+ if (t.limit > round) {
104
+ rotationList.push(t.token);
105
+ }
106
+ }
185
107
  }
186
- return Math.min(
187
- configuredMaximumPreparingIssuesCount ?? rotationTokenSlots.length,
188
- rotationTokenSlots.length,
189
- );
108
+
109
+ return { tokens: rotationList, effectiveCap };
190
110
  };
191
111
 
192
112
  buildRotationOrder = (
@@ -195,19 +115,17 @@ export class StartPreparationUseCase {
195
115
  modelName: string | null,
196
116
  ): RotationOrderEntry[] => {
197
117
  const weeklyLimitType = this.weeklyLimitTypeForModel(modelName);
198
- const isWeeklyLimitRejected = (usage: ClaudeTokenUsage): boolean =>
199
- usage.modelWeeklyLimits[weeklyLimitType]?.rejected === true ||
200
- usage.modelWeeklyLimits['seven_day']?.rejected === true;
201
118
  const selectedTokens = tokenUsages
202
119
  .filter((usage) => !usage.blocked)
203
120
  .filter((usage) => !usage.rejected)
204
- .filter((usage) => !isWeeklyLimitRejected(usage))
121
+ .filter(
122
+ (usage) => !this.isModelWeeklyLimitRejected(usage, weeklyLimitType),
123
+ )
205
124
  .filter(
206
125
  (usage) =>
207
- this.maximumPreparingProcessCountForToken(usage.fiveHourUtilization) >
208
- 0,
126
+ usage.fiveHourUtilization * 100 < utilizationPercentageThreshold,
209
127
  )
210
- .sort((a, b) => a.fiveHourUtilization - b.fiveHourUtilization);
128
+ .sort((a, b) => a.sevenDayUtilization - b.sevenDayUtilization);
211
129
  const selectedTokenValues = new Set(selectedTokens.map((u) => u.token));
212
130
  const excluded: RotationOrderEntry[] = tokenUsages
213
131
  .filter((usage) => !selectedTokenValues.has(usage.token))
@@ -219,10 +137,8 @@ export class StartPreparationUseCase {
219
137
  thresholdExcluded:
220
138
  !usage.blocked &&
221
139
  !usage.rejected &&
222
- !isWeeklyLimitRejected(usage) &&
223
- this.maximumPreparingProcessCountForToken(
224
- usage.fiveHourUtilization,
225
- ) === 0,
140
+ !this.isModelWeeklyLimitRejected(usage, weeklyLimitType) &&
141
+ usage.fiveHourUtilization * 100 >= utilizationPercentageThreshold,
226
142
  }));
227
143
  const selectedEntries: RotationOrderEntry[] = selectedTokens.map(
228
144
  (usage) => ({
@@ -250,8 +166,7 @@ export class StartPreparationUseCase {
250
166
  }): Promise<{ rotationOrder: RotationOrderEntry[] | null }> => {
251
167
  const tokenUsages =
252
168
  await this.claudeTokenUsageRepository.getAvailableTokenUsages();
253
- let rotationTokenSlots: Array<{ token: string; model: string }> | null =
254
- null;
169
+ let rotationTokens: string[] | null = null;
255
170
  let proxyBaseUrl: string | null = null;
256
171
  const rotationOrder: RotationOrderEntry[] | null =
257
172
  tokenUsages.length > 0
@@ -261,25 +176,27 @@ export class StartPreparationUseCase {
261
176
  params.defaultLlmModelName,
262
177
  )
263
178
  : null;
179
+ const maximumPreparingIssuesCount =
180
+ params.maximumPreparingIssuesCount ?? SEVEN_DAY_NORMAL_CONCURRENT_LIMIT;
181
+ let effectiveMaxPreparingIssuesCount = maximumPreparingIssuesCount;
264
182
  if (tokenUsages.length > 0) {
265
- const ranked = this.selectRotationTokens(
183
+ const { tokens: ranked, effectiveCap } = this.selectRotationTokens(
266
184
  tokenUsages,
185
+ params.utilizationPercentageThreshold,
267
186
  params.defaultLlmModelName,
187
+ maximumPreparingIssuesCount,
268
188
  );
269
189
  if (ranked.length === 0) {
270
190
  console.warn(
271
- `All ${tokenUsages.length} configured Claude OAuth token(s) are unavailable (blocked, rejected, all model weekly limits exhausted, or 5h utilization >= 95%). Skipping starting preparation.`,
191
+ `All ${tokenUsages.length} configured Claude OAuth token(s) are unavailable (blocked, rejected, weekly limit for ${this.weeklyLimitTypeForModel(params.defaultLlmModelName)} exhausted, or 5h utilization >= ${params.utilizationPercentageThreshold}%). Skipping starting preparation.`,
272
192
  );
273
193
  return { rotationOrder };
274
194
  }
275
195
  await this.claudeTokenUsageRepository.ensureObservable();
276
- rotationTokenSlots = this.createRotationTokenSlots(ranked);
196
+ rotationTokens = ranked;
197
+ effectiveMaxPreparingIssuesCount = effectiveCap;
277
198
  proxyBaseUrl = this.claudeTokenUsageRepository.proxyBaseUrl();
278
199
  }
279
- const maximumPreparingIssuesCount = this.resolveMaximumPreparingIssuesCount(
280
- params.maximumPreparingIssuesCount,
281
- rotationTokenSlots,
282
- );
283
200
 
284
201
  const project = await this.projectRepository.getByUrl(params.projectUrl);
285
202
  const storyObjectMap = await this.issueRepository.getStoryObjectMap(
@@ -328,7 +245,7 @@ export class StartPreparationUseCase {
328
245
  for (
329
246
  let i = 0;
330
247
  i < awaitingWorkspaceIssues.length &&
331
- updatedCurrentPreparationIssueCount < maximumPreparingIssuesCount;
248
+ updatedCurrentPreparationIssueCount < effectiveMaxPreparingIssuesCount;
332
249
  i++
333
250
  ) {
334
251
  const issue = awaitingWorkspaceIssues[i];
@@ -348,15 +265,6 @@ export class StartPreparationUseCase {
348
265
  params.allowedIssueAuthors !== null &&
349
266
  !params.allowedIssueAuthors.includes(issue.author)
350
267
  ) {
351
- if (issue.author === '') {
352
- console.warn(
353
- `Skipping issue ${issue.url}: author is unknown (empty string); deny-by-default when allowedIssueAuthors is configured.`,
354
- );
355
- } else {
356
- console.warn(
357
- `Skipping issue ${issue.url}: author '${issue.author}' is not in the allowedIssueAuthors list.`,
358
- );
359
- }
360
268
  continue;
361
269
  }
362
270
  const agent =
@@ -375,7 +283,7 @@ export class StartPreparationUseCase {
375
283
  .find((label: string) => label.startsWith('llm-model:'))
376
284
  ?.replace('llm-model:', '')
377
285
  .trim() || params.defaultLlmModelName;
378
- if (!model && rotationTokenSlots === null) {
286
+ if (!model) {
379
287
  console.error(
380
288
  `No LLM model configured for issue ${issue.url}. Provide --defaultLlmModelName or add an llm-model: label.`,
381
289
  );
@@ -458,25 +366,10 @@ export class StartPreparationUseCase {
458
366
  );
459
367
  issue.status = PREPARATION_STATUS_NAME;
460
368
 
461
- let spawnEnv: Record<string, string> | undefined;
462
- let spawnModel = model ?? '';
463
- if (rotationTokenSlots !== null && proxyBaseUrl !== null) {
464
- const selected =
465
- rotationTokenSlots[
466
- (currentPreparationIssueCount + startedInThisRunCount) %
467
- rotationTokenSlots.length
468
- ];
469
- spawnModel = selected.model;
470
- spawnEnv = {
471
- CLAUDE_CODE_OAUTH_TOKEN: selected.token,
472
- ANTHROPIC_BASE_URL: proxyBaseUrl,
473
- };
474
- }
475
-
476
369
  const awArgs: string[] = [
477
370
  issue.url,
478
371
  agent,
479
- spawnModel,
372
+ model,
480
373
  '--configFilePath',
481
374
  params.configFilePath,
482
375
  '--branch',
@@ -492,6 +385,18 @@ export class StartPreparationUseCase {
492
385
  ];
493
386
  awArgs.push('--codexHome', codexHome);
494
387
  }
388
+ let spawnEnv: Record<string, string> | undefined;
389
+ if (rotationTokens !== null && proxyBaseUrl !== null) {
390
+ const selected =
391
+ rotationTokens[
392
+ (currentPreparationIssueCount + startedInThisRunCount) %
393
+ rotationTokens.length
394
+ ];
395
+ spawnEnv = {
396
+ CLAUDE_CODE_OAUTH_TOKEN: selected,
397
+ ANTHROPIC_BASE_URL: proxyBaseUrl,
398
+ };
399
+ }
495
400
  await this.localCommandRunner.runCommand(
496
401
  'aw',
497
402
  awArgs,
@@ -1 +1 @@
1
- {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AA2B3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAkPP;CACH"}
1
+ {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AA2B3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAmPP;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProxyClaudeTokenUsageRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qEAAqE,CAAC;AAKjH,qBAAa,+BAAgC,YAAW,0BAA0B;IAE9E,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,IAAI,GAAE,MAAmB;IAG5C,gBAAgB,QAAa,OAAO,CAAC,IAAI,CAAC,CAExC;IAEF,uBAAuB,QAAa,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA0D7D;IAEF,YAAY,QAAO,MAAM,CAAoC;CAC9D"}
1
+ {"version":3,"file":"ProxyClaudeTokenUsageRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qEAAqE,CAAC;AAKjH,qBAAa,+BAAgC,YAAW,0BAA0B;IAE9E,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,IAAI,GAAE,MAAmB;IAG5C,gBAAgB,QAAa,OAAO,CAAC,IAAI,CAAC,CAExC;IAEF,uBAAuB,QAAa,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA+D7D;IAEF,YAAY,QAAO,MAAM,CAAoC;CAC9D"}
@@ -6,6 +6,7 @@ export type ClaudeTokenUsage = {
6
6
  name?: string;
7
7
  token: string;
8
8
  fiveHourUtilization: number;
9
+ sevenDayUtilization: number;
9
10
  blocked: boolean;
10
11
  rejected: boolean;
11
12
  modelWeeklyLimits: Record<string, ClaudeModelWeeklyLimit>;
@@ -1 +1 @@
1
- {"version":3,"file":"ClaudeTokenUsage.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/ClaudeTokenUsage.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CAC3D,CAAC"}
1
+ {"version":3,"file":"ClaudeTokenUsage.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/ClaudeTokenUsage.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CAC3D,CAAC"}
@@ -1,6 +1,5 @@
1
1
  import { FieldOption } from './Project';
2
2
  export declare const DEFAULT_STATUS_NAME = "Unread";
3
- export declare const AWAITING_TASK_BREAKDOWN_STATUS_NAME = "Awaiting Task Breakdown";
4
3
  export declare const AWAITING_WORKSPACE_STATUS_NAME = "Awaiting Workspace";
5
4
  export declare const PREPARATION_STATUS_NAME = "Preparation";
6
5
  export declare const FAILED_PREPARATION_STATUS_NAME = "Failed Preparation";
@@ -12,6 +11,7 @@ export declare const DONE_STATUS_NAME = "Done";
12
11
  export declare const ICEBOX_STATUS_NAME = "Icebox";
13
12
  export declare const LEGACY_TODO_STATUS_NAME = "Todo";
14
13
  export declare const LEGACY_IN_TMUX_STATUS_NAME = "In Tmux";
14
+ export declare const LEGACY_AWAITING_TASK_BREAKDOWN_STATUS_NAME = "Awaiting Task Breakdown";
15
15
  export type WorkflowStatusDefinition = {
16
16
  name: string;
17
17
  color: FieldOption['color'];
@@ -1 +1 @@
1
- {"version":3,"file":"WorkflowStatus.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/WorkflowStatus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,mCAAmC,4BAA4B,CAAC;AAC7E,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AACrD,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,kCAAkC,2BAA2B,CAAC;AAC3E,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAC7C,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AACtD,eAAO,MAAM,gBAAgB,SAAS,CAAC;AACvC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,eAAO,MAAM,uBAAuB,SAAS,CAAC;AAC9C,eAAO,MAAM,0BAA0B,YAAY,CAAC;AAEpD,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,wBAAwB,EAyChE,CAAC"}
1
+ {"version":3,"file":"WorkflowStatus.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/WorkflowStatus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AACrD,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,kCAAkC,2BAA2B,CAAC;AAC3E,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAC7C,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AACtD,eAAO,MAAM,gBAAgB,SAAS,CAAC;AACvC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,eAAO,MAAM,uBAAuB,SAAS,CAAC;AAC9C,eAAO,MAAM,0BAA0B,YAAY,CAAC;AACpD,eAAO,MAAM,0CAA0C,4BAC5B,CAAC;AAE5B,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,wBAAwB,EAqChE,CAAC"}
@@ -1,7 +1,9 @@
1
1
  import { ProjectRepository } from './adapter-interfaces/ProjectRepository';
2
+ import { IssueRepository } from './adapter-interfaces/IssueRepository';
2
3
  export declare class SetupTowerDefenceProjectUseCase {
3
4
  private readonly projectRepository;
4
- constructor(projectRepository: Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>);
5
+ private readonly issueRepository;
6
+ constructor(projectRepository: Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>, issueRepository: Pick<IssueRepository, 'getAllIssues' | 'updateStatus'>);
5
7
  private static readonly LEGACY_STATUS_NAMES;
6
8
  private static readonly MIGRATED_FROM_NAMES;
7
9
  run: (params: {
@@ -1 +1 @@
1
- {"version":3,"file":"SetupTowerDefenceProjectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/SetupTowerDefenceProjectUseCase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAW3E,qBAAa,+BAA+B;IAExC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAAjB,iBAAiB,EAAE,IAAI,CACtC,iBAAiB,EACjB,UAAU,GAAG,kBAAkB,CAChC;IAGH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAKzC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAIxC;IAEH,GAAG,GAAU,QAAQ;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,IAAI,CAAC,CAoDzD;IAEF,OAAO,CAAC,MAAM,CAAC,mCAAmC,CAgBhD;CACH"}
1
+ {"version":3,"file":"SetupTowerDefenceProjectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/SetupTowerDefenceProjectUseCase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAYvE,qBAAa,+BAA+B;IAExC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAIlC,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAJf,iBAAiB,EAAE,IAAI,CACtC,iBAAiB,EACjB,UAAU,GAAG,kBAAkB,CAChC,EACgB,eAAe,EAAE,IAAI,CACpC,eAAe,EACf,cAAc,GAAG,cAAc,CAChC;IAGH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAKzC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAKxC;IAEH,GAAG,GAAU,QAAQ;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,IAAI,CAAC,CA4EzD;IAEF,OAAO,CAAC,MAAM,CAAC,mCAAmC,CAgBhD;CACH"}
@@ -16,14 +16,10 @@ export declare class StartPreparationUseCase {
16
16
  private readonly localCommandRunner;
17
17
  private readonly claudeTokenUsageRepository;
18
18
  constructor(projectRepository: Pick<ProjectRepository, 'getByUrl'>, issueRepository: Pick<IssueRepository, 'getStoryObjectMap' | 'updateStatus' | 'findRelatedOpenPRs' | 'getOpenPullRequest' | 'closePullRequest' | 'deletePullRequestBranch' | 'createCommentByUrl'>, localCommandRunner: LocalCommandRunner, claudeTokenUsageRepository: ClaudeTokenUsageRepository);
19
- private readonly KNOWN_MODEL_SPECIFIC_LIMIT_TYPES;
20
19
  private weeklyLimitTypeForModel;
21
- private deriveFallbackModelName;
22
- private selectModelForToken;
23
- private maximumPreparingProcessCountForToken;
20
+ private isModelWeeklyLimitRejected;
21
+ private getTokenConcurrentLimit;
24
22
  private selectRotationTokens;
25
- private createRotationTokenSlots;
26
- private resolveMaximumPreparingIssuesCount;
27
23
  buildRotationOrder: (tokenUsages: ClaudeTokenUsage[], utilizationPercentageThreshold: number, modelName: string | null) => RotationOrderEntry[];
28
24
  run: (params: {
29
25
  projectUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"StartPreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/StartPreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAiBhE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAUhC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,0BAA0B;gBAZ1B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACtD,eAAe,EAAE,IAAI,CACpC,eAAe,EACb,mBAAmB,GACnB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,yBAAyB,GACzB,oBAAoB,CACvB,EACgB,kBAAkB,EAAE,kBAAkB,EACtC,0BAA0B,EAAE,0BAA0B;IAGzE,OAAO,CAAC,QAAQ,CAAC,gCAAgC,CAG/C;IAEF,OAAO,CAAC,uBAAuB,CAK7B;IAEF,OAAO,CAAC,uBAAuB,CAoB7B;IAEF,OAAO,CAAC,mBAAmB,CAyBzB;IAEF,OAAO,CAAC,oCAAoC,CAqB1C;IAEF,OAAO,CAAC,oBAAoB,CA6B1B;IAEF,OAAO,CAAC,wBAAwB,CAe9B;IAEF,OAAO,CAAC,kCAAkC,CAWxC;IAEF,kBAAkB,GAChB,aAAa,gBAAgB,EAAE,EAC/B,gCAAgC,MAAM,EACtC,WAAW,MAAM,GAAG,IAAI,KACvB,kBAAkB,EAAE,CAyCrB;IAEF,GAAG,GAAU,QAAQ;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;QACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,cAAc,EAAE,MAAM,CAAC;QACvB,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3C,8BAA8B,EAAE,MAAM,CAAC;QACvC,mBAAmB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACrC,mBAAmB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACrC,sBAAsB,EAAE,MAAM,CAAC;KAChC,KAAG,OAAO,CAAC;QAAE,aAAa,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAA;KAAE,CAAC,CA8PzD;CACH"}
1
+ {"version":3,"file":"StartPreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/StartPreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAShE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAUhC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,0BAA0B;gBAZ1B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACtD,eAAe,EAAE,IAAI,CACpC,eAAe,EACb,mBAAmB,GACnB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,yBAAyB,GACzB,oBAAoB,CACvB,EACgB,kBAAkB,EAAE,kBAAkB,EACtC,0BAA0B,EAAE,0BAA0B;IAGzE,OAAO,CAAC,uBAAuB,CAK7B;IAEF,OAAO,CAAC,0BAA0B,CAQhC;IAEF,OAAO,CAAC,uBAAuB,CAU7B;IAEF,OAAO,CAAC,oBAAoB,CA0C1B;IAEF,kBAAkB,GAChB,aAAa,gBAAgB,EAAE,EAC/B,gCAAgC,MAAM,EACtC,WAAW,MAAM,GAAG,IAAI,KACvB,kBAAkB,EAAE,CAqCrB;IAEF,GAAG,GAAU,QAAQ;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;QACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,cAAc,EAAE,MAAM,CAAC;QACvB,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3C,8BAA8B,EAAE,MAAM,CAAC;QACvC,mBAAmB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACrC,mBAAmB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACrC,sBAAsB,EAAE,MAAM,CAAC;KAChC,KAAG,OAAO,CAAC;QAAE,aAAa,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAA;KAAE,CAAC,CAmPzD;CACH"}